Skip to content

Commit 7bf8f6f

Browse files
committed
PR42104: Support instantiations of lambdas that implicitly capture
packs. Two changes: * Track odr-use via FunctionParmPackExprs to properly handle dependent odr-uses of packs in generic lambdas. * Do not instantiate implicit captures; instead, regenerate them by instantiating the body of the lambda. This is necessary to distinguish between cases where only one element of a pack is captured and cases where the entire pack is captured. This reinstates r362358 (reverted in r362375) with a fix for an uninitialized variable use in UpdateMarkingForLValueToRValue. llvm-svn: 362531
1 parent f4302ad commit 7bf8f6f

File tree

9 files changed

+161
-67
lines changed

9 files changed

+161
-67
lines changed

clang/include/clang/Sema/ScopeInfo.h

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define LLVM_CLANG_SEMA_SCOPEINFO_H
1616

1717
#include "clang/AST/Expr.h"
18+
#include "clang/AST/ExprCXX.h"
1819
#include "clang/AST/Type.h"
1920
#include "clang/Basic/CapturedStmt.h"
2021
#include "clang/Basic/LLVM.h"
@@ -913,7 +914,8 @@ class LambdaScopeInfo final : public CapturingScopeInfo {
913914
/// };
914915
/// }
915916
void addPotentialCapture(Expr *VarExpr) {
916-
assert(isa<DeclRefExpr>(VarExpr) || isa<MemberExpr>(VarExpr));
917+
assert(isa<DeclRefExpr>(VarExpr) || isa<MemberExpr>(VarExpr) ||
918+
isa<FunctionParmPackExpr>(VarExpr));
917919
PotentiallyCapturingExprs.push_back(VarExpr);
918920
}
919921

@@ -965,13 +967,15 @@ class LambdaScopeInfo final : public CapturingScopeInfo {
965967
/// building such a node. So we need a rule that anyone can implement and get
966968
/// exactly the same result".
967969
void markVariableExprAsNonODRUsed(Expr *CapturingVarExpr) {
968-
assert(isa<DeclRefExpr>(CapturingVarExpr)
969-
|| isa<MemberExpr>(CapturingVarExpr));
970+
assert(isa<DeclRefExpr>(CapturingVarExpr) ||
971+
isa<MemberExpr>(CapturingVarExpr) ||
972+
isa<FunctionParmPackExpr>(CapturingVarExpr));
970973
NonODRUsedCapturingExprs.insert(CapturingVarExpr);
971974
}
972975
bool isVariableExprMarkedAsNonODRUsed(Expr *CapturingVarExpr) const {
973-
assert(isa<DeclRefExpr>(CapturingVarExpr)
974-
|| isa<MemberExpr>(CapturingVarExpr));
976+
assert(isa<DeclRefExpr>(CapturingVarExpr) ||
977+
isa<MemberExpr>(CapturingVarExpr) ||
978+
isa<FunctionParmPackExpr>(CapturingVarExpr));
975979
return NonODRUsedCapturingExprs.count(CapturingVarExpr);
976980
}
977981
void removePotentialCapture(Expr *E) {
@@ -993,9 +997,8 @@ class LambdaScopeInfo final : public CapturingScopeInfo {
993997
PotentialThisCaptureLocation.isValid();
994998
}
995999

996-
// When passed the index, returns the VarDecl and Expr associated
997-
// with the index.
998-
void getPotentialVariableCapture(unsigned Idx, VarDecl *&VD, Expr *&E) const;
1000+
void visitPotentialCaptures(
1001+
llvm::function_ref<void(VarDecl *, Expr *)> Callback) const;
9991002
};
10001003

10011004
FunctionScopeInfo::WeakObjectProfileTy::WeakObjectProfileTy()

clang/include/clang/Sema/Sema.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4181,6 +4181,7 @@ class Sema {
41814181
void MarkVariableReferenced(SourceLocation Loc, VarDecl *Var);
41824182
void MarkDeclRefReferenced(DeclRefExpr *E, const Expr *Base = nullptr);
41834183
void MarkMemberReferenced(MemberExpr *E);
4184+
void MarkFunctionParmPackReferenced(FunctionParmPackExpr *E);
41844185
void MarkCaptureUsedInEnclosingContext(VarDecl *Capture, SourceLocation Loc,
41854186
unsigned CapturingScopeIndex);
41864187

clang/lib/Sema/ScopeInfo.cpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -229,20 +229,20 @@ bool CapturingScopeInfo::isVLATypeCaptured(const VariableArrayType *VAT) const {
229229
return false;
230230
}
231231

232-
void LambdaScopeInfo::getPotentialVariableCapture(unsigned Idx, VarDecl *&VD,
233-
Expr *&E) const {
234-
assert(Idx < getNumPotentialVariableCaptures() &&
235-
"Index of potential capture must be within 0 to less than the "
236-
"number of captures!");
237-
E = PotentiallyCapturingExprs[Idx];
238-
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
239-
VD = dyn_cast<VarDecl>(DRE->getFoundDecl());
240-
else if (MemberExpr *ME = dyn_cast<MemberExpr>(E))
241-
VD = dyn_cast<VarDecl>(ME->getMemberDecl());
242-
else
243-
llvm_unreachable("Only DeclRefExprs or MemberExprs should be added for "
244-
"potential captures");
245-
assert(VD);
232+
void LambdaScopeInfo::visitPotentialCaptures(
233+
llvm::function_ref<void(VarDecl *, Expr *)> Callback) const {
234+
for (Expr *E : PotentiallyCapturingExprs) {
235+
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
236+
Callback(cast<VarDecl>(DRE->getFoundDecl()), E);
237+
} else if (auto *ME = dyn_cast<MemberExpr>(E)) {
238+
Callback(cast<VarDecl>(ME->getMemberDecl()), E);
239+
} else if (auto *FP = dyn_cast<FunctionParmPackExpr>(E)) {
240+
for (VarDecl *VD : *FP)
241+
Callback(VD, E);
242+
} else {
243+
llvm_unreachable("unexpected expression in potential captures list");
244+
}
245+
}
246246
}
247247

248248
FunctionScopeInfo::~FunctionScopeInfo() { }

clang/lib/Sema/SemaExpr.cpp

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14610,7 +14610,9 @@ namespace {
1461014610
// context so never needs to be transformed.
1461114611
// FIXME: Ideally we wouldn't transform the closure type either, and would
1461214612
// just recreate the capture expressions and lambda expression.
14613-
StmtResult TransformLambdaBody(Stmt *Body) { return Body; }
14613+
StmtResult TransformLambdaBody(LambdaExpr *E, Stmt *Body) {
14614+
return SkipLambdaBody(E, Body);
14615+
}
1461414616
};
1461514617
}
1461614618

@@ -15054,7 +15056,7 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
1505415056
/// *FunctionScopeIndexToStopAt on the FunctionScopeInfo stack.
1505515057
static void
1505615058
MarkVarDeclODRUsed(VarDecl *Var, SourceLocation Loc, Sema &SemaRef,
15057-
const unsigned *const FunctionScopeIndexToStopAt) {
15059+
const unsigned *const FunctionScopeIndexToStopAt = nullptr) {
1505815060
// Keep track of used but undefined variables.
1505915061
// FIXME: We shouldn't suppress this warning for static data members.
1506015062
if (Var->hasDefinition(SemaRef.Context) == VarDecl::DeclarationOnly &&
@@ -15735,14 +15737,21 @@ void Sema::UpdateMarkingForLValueToRValue(Expr *E) {
1573515737
// variable.
1573615738
if (LambdaScopeInfo *LSI = getCurLambda()) {
1573715739
Expr *SansParensExpr = E->IgnoreParens();
15738-
VarDecl *Var = nullptr;
15740+
VarDecl *Var;
15741+
ArrayRef<VarDecl *> Vars(&Var, &Var + 1);
1573915742
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(SansParensExpr))
1574015743
Var = dyn_cast<VarDecl>(DRE->getFoundDecl());
1574115744
else if (MemberExpr *ME = dyn_cast<MemberExpr>(SansParensExpr))
1574215745
Var = dyn_cast<VarDecl>(ME->getMemberDecl());
15746+
else if (auto *FPPE = dyn_cast<FunctionParmPackExpr>(SansParensExpr))
15747+
Vars = llvm::makeArrayRef(FPPE->begin(), FPPE->end());
15748+
else
15749+
Vars = None;
1574315750

15744-
if (Var && IsVariableNonDependentAndAConstantExpression(Var, Context))
15745-
LSI->markVariableExprAsNonODRUsed(SansParensExpr);
15751+
for (VarDecl *VD : Vars) {
15752+
if (VD && IsVariableNonDependentAndAConstantExpression(VD, Context))
15753+
LSI->markVariableExprAsNonODRUsed(SansParensExpr);
15754+
}
1574615755
}
1574715756
}
1574815757

@@ -15767,20 +15776,18 @@ void Sema::CleanupVarDeclMarking() {
1576715776
std::swap(LocalMaybeODRUseExprs, MaybeODRUseExprs);
1576815777

1576915778
for (Expr *E : LocalMaybeODRUseExprs) {
15770-
VarDecl *Var;
15771-
SourceLocation Loc;
15772-
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
15773-
Var = cast<VarDecl>(DRE->getDecl());
15774-
Loc = DRE->getLocation();
15775-
} else if (MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
15776-
Var = cast<VarDecl>(ME->getMemberDecl());
15777-
Loc = ME->getMemberLoc();
15779+
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
15780+
MarkVarDeclODRUsed(cast<VarDecl>(DRE->getDecl()),
15781+
DRE->getLocation(), *this);
15782+
} else if (auto *ME = dyn_cast<MemberExpr>(E)) {
15783+
MarkVarDeclODRUsed(cast<VarDecl>(ME->getMemberDecl()), ME->getMemberLoc(),
15784+
*this);
15785+
} else if (auto *FP = dyn_cast<FunctionParmPackExpr>(E)) {
15786+
for (VarDecl *VD : *FP)
15787+
MarkVarDeclODRUsed(VD, FP->getParameterPackLocation(), *this);
1577815788
} else {
1577915789
llvm_unreachable("Unexpected expression");
1578015790
}
15781-
15782-
MarkVarDeclODRUsed(Var, Loc, *this,
15783-
/*MaxFunctionScopeIndex Pointer*/ nullptr);
1578415791
}
1578515792

1578615793
assert(MaybeODRUseExprs.empty() &&
@@ -15789,7 +15796,8 @@ void Sema::CleanupVarDeclMarking() {
1578915796

1579015797
static void DoMarkVarDeclReferenced(Sema &SemaRef, SourceLocation Loc,
1579115798
VarDecl *Var, Expr *E) {
15792-
assert((!E || isa<DeclRefExpr>(E) || isa<MemberExpr>(E)) &&
15799+
assert((!E || isa<DeclRefExpr>(E) || isa<MemberExpr>(E) ||
15800+
isa<FunctionParmPackExpr>(E)) &&
1579315801
"Invalid Expr argument to DoMarkVarDeclReferenced");
1579415802
Var->setReferenced();
1579515803

@@ -16022,6 +16030,12 @@ void Sema::MarkMemberReferenced(MemberExpr *E) {
1602216030
MarkExprReferenced(*this, Loc, E->getMemberDecl(), E, MightBeOdrUse);
1602316031
}
1602416032

16033+
/// Perform reference-marking and odr-use handling for a FunctionParmPackExpr.
16034+
void Sema::MarkFunctionParmPackReferenced(FunctionParmPackExpr *E) {
16035+
for (VarDecl *VD : *E)
16036+
MarkExprReferenced(*this, E->getParameterPackLocation(), VD, E, true);
16037+
}
16038+
1602516039
/// Perform marking for a reference to an arbitrary declaration. It
1602616040
/// marks the declaration referenced, and performs odr-use checking for
1602716041
/// functions and variables. This method should not be used when building a

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7427,12 +7427,7 @@ static void CheckIfAnyEnclosingLambdasMustCaptureAnyPotentialCaptures(
74277427
// All the potentially captureable variables in the current nested
74287428
// lambda (within a generic outer lambda), must be captured by an
74297429
// outer lambda that is enclosed within a non-dependent context.
7430-
const unsigned NumPotentialCaptures =
7431-
CurrentLSI->getNumPotentialVariableCaptures();
7432-
for (unsigned I = 0; I != NumPotentialCaptures; ++I) {
7433-
Expr *VarExpr = nullptr;
7434-
VarDecl *Var = nullptr;
7435-
CurrentLSI->getPotentialVariableCapture(I, Var, VarExpr);
7430+
CurrentLSI->visitPotentialCaptures([&] (VarDecl *Var, Expr *VarExpr) {
74367431
// If the variable is clearly identified as non-odr-used and the full
74377432
// expression is not instantiation dependent, only then do we not
74387433
// need to check enclosing lambda's for speculative captures.
@@ -7446,7 +7441,7 @@ static void CheckIfAnyEnclosingLambdasMustCaptureAnyPotentialCaptures(
74467441
// }
74477442
if (CurrentLSI->isVariableExprMarkedAsNonODRUsed(VarExpr) &&
74487443
!IsFullExprInstantiationDependent)
7449-
continue;
7444+
return;
74507445

74517446
// If we have a capture-capable lambda for the variable, go ahead and
74527447
// capture the variable in that lambda (and all its enclosing lambdas).
@@ -7478,7 +7473,7 @@ static void CheckIfAnyEnclosingLambdasMustCaptureAnyPotentialCaptures(
74787473
DeclRefType, nullptr);
74797474
}
74807475
}
7481-
}
7476+
});
74827477

74837478
// Check if 'this' needs to be captured.
74847479
if (CurrentLSI->hasPotentialThisCapture()) {

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1368,9 +1368,11 @@ TemplateInstantiator::TransformFunctionParmPackExpr(FunctionParmPackExpr *E) {
13681368
Vars.push_back(D);
13691369
}
13701370

1371-
return FunctionParmPackExpr::Create(getSema().Context, T,
1372-
E->getParameterPack(),
1373-
E->getParameterPackLocation(), Vars);
1371+
auto *PackExpr =
1372+
FunctionParmPackExpr::Create(getSema().Context, T, E->getParameterPack(),
1373+
E->getParameterPackLocation(), Vars);
1374+
getSema().MarkFunctionParmPackReferenced(PackExpr);
1375+
return PackExpr;
13741376
}
13751377

13761378
ExprResult
@@ -1389,8 +1391,10 @@ TemplateInstantiator::TransformFunctionParmPackRefExpr(DeclRefExpr *E,
13891391
QualType T = TransformType(E->getType());
13901392
if (T.isNull())
13911393
return ExprError();
1392-
return FunctionParmPackExpr::Create(getSema().Context, T, PD,
1393-
E->getExprLoc(), *Pack);
1394+
auto *PackExpr = FunctionParmPackExpr::Create(getSema().Context, T, PD,
1395+
E->getExprLoc(), *Pack);
1396+
getSema().MarkFunctionParmPackReferenced(PackExpr);
1397+
return PackExpr;
13941398
}
13951399

13961400
TransformedDecl = (*Pack)[getSema().ArgumentPackSubstitutionIndex];

clang/lib/Sema/TreeTransform.h

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,10 @@ class TreeTransform {
660660
bool ExpectParameterPack);
661661

662662
/// Transform the body of a lambda-expression.
663-
StmtResult TransformLambdaBody(Stmt *Body);
663+
StmtResult TransformLambdaBody(LambdaExpr *E, Stmt *Body);
664+
/// Alternative implementation of TransformLambdaBody that skips transforming
665+
/// the body.
666+
StmtResult SkipLambdaBody(LambdaExpr *E, Stmt *Body);
664667

665668
QualType TransformReferenceType(TypeLocBuilder &TLB, ReferenceTypeLoc TL);
666669

@@ -11370,16 +11373,13 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
1137011373
bool Invalid = false;
1137111374

1137211375
// Transform captures.
11373-
bool FinishedExplicitCaptures = false;
1137411376
for (LambdaExpr::capture_iterator C = E->capture_begin(),
1137511377
CEnd = E->capture_end();
1137611378
C != CEnd; ++C) {
1137711379
// When we hit the first implicit capture, tell Sema that we've finished
1137811380
// the list of explicit captures.
11379-
if (!FinishedExplicitCaptures && C->isImplicit()) {
11380-
getSema().finishLambdaExplicitCaptures(LSI);
11381-
FinishedExplicitCaptures = true;
11382-
}
11381+
if (C->isImplicit())
11382+
break;
1138311383

1138411384
// Capturing 'this' is trivial.
1138511385
if (C->capturesThis()) {
@@ -11488,17 +11488,16 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
1148811488
getSema().tryCaptureVariable(CapturedVar, C->getLocation(), Kind,
1148911489
EllipsisLoc);
1149011490
}
11491-
if (!FinishedExplicitCaptures)
11492-
getSema().finishLambdaExplicitCaptures(LSI);
11491+
getSema().finishLambdaExplicitCaptures(LSI);
1149311492

11494-
// Enter a new evaluation context to insulate the lambda from any
11495-
// cleanups from the enclosing full-expression.
11493+
// FIXME: Sema's lambda-building mechanism expects us to push an expression
11494+
// evaluation context even if we're not transforming the function body.
1149611495
getSema().PushExpressionEvaluationContext(
1149711496
Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
1149811497

1149911498
// Instantiate the body of the lambda expression.
1150011499
StmtResult Body =
11501-
Invalid ? StmtError() : getDerived().TransformLambdaBody(E->getBody());
11500+
Invalid ? StmtError() : getDerived().TransformLambdaBody(E, E->getBody());
1150211501

1150311502
// ActOnLambda* will pop the function scope for us.
1150411503
FuncScopeCleanup.disable();
@@ -11524,10 +11523,50 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
1152411523

1152511524
template<typename Derived>
1152611525
StmtResult
11527-
TreeTransform<Derived>::TransformLambdaBody(Stmt *S) {
11526+
TreeTransform<Derived>::TransformLambdaBody(LambdaExpr *E, Stmt *S) {
1152811527
return TransformStmt(S);
1152911528
}
1153011529

11530+
template<typename Derived>
11531+
StmtResult
11532+
TreeTransform<Derived>::SkipLambdaBody(LambdaExpr *E, Stmt *S) {
11533+
// Transform captures.
11534+
for (LambdaExpr::capture_iterator C = E->capture_begin(),
11535+
CEnd = E->capture_end();
11536+
C != CEnd; ++C) {
11537+
// When we hit the first implicit capture, tell Sema that we've finished
11538+
// the list of explicit captures.
11539+
if (!C->isImplicit())
11540+
continue;
11541+
11542+
// Capturing 'this' is trivial.
11543+
if (C->capturesThis()) {
11544+
getSema().CheckCXXThisCapture(C->getLocation(), C->isExplicit(),
11545+
/*BuildAndDiagnose*/ true, nullptr,
11546+
C->getCaptureKind() == LCK_StarThis);
11547+
continue;
11548+
}
11549+
// Captured expression will be recaptured during captured variables
11550+
// rebuilding.
11551+
if (C->capturesVLAType())
11552+
continue;
11553+
11554+
assert(C->capturesVariable() && "unexpected kind of lambda capture");
11555+
assert(!E->isInitCapture(C) && "implicit init-capture?");
11556+
11557+
// Transform the captured variable.
11558+
VarDecl *CapturedVar = cast_or_null<VarDecl>(
11559+
getDerived().TransformDecl(C->getLocation(), C->getCapturedVar()));
11560+
if (!CapturedVar || CapturedVar->isInvalidDecl())
11561+
return StmtError();
11562+
11563+
// Capture the transformed variable.
11564+
getSema().tryCaptureVariable(CapturedVar, C->getLocation());
11565+
}
11566+
11567+
return S;
11568+
}
11569+
1153111570
template<typename Derived>
1153211571
ExprResult
1153311572
TreeTransform<Derived>::TransformCXXUnresolvedConstructExpr(

0 commit comments

Comments
 (0)