Skip to content

Commit 996b092

Browse files
committed
[analyzer] Lambda capture non-POD type array
This patch introduces a new `ConstructionContext` for lambda capture. This `ConstructionContext` allows the analyzer to construct the captured object directly into it's final region, and makes it possible to capture non-POD arrays. Differential Revision: https://reviews.llvm.org/D129967
1 parent ae72cc7 commit 996b092

File tree

8 files changed

+147
-20
lines changed

8 files changed

+147
-20
lines changed

clang/include/clang/Analysis/CFG.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ class CFGCXXRecordTypedCall : public CFGStmt {
202202
isa<ReturnedValueConstructionContext>(C) ||
203203
isa<VariableConstructionContext>(C) ||
204204
isa<ConstructorInitializerConstructionContext>(C) ||
205-
isa<ArgumentConstructionContext>(C)));
205+
isa<ArgumentConstructionContext>(C) ||
206+
isa<LambdaCaptureConstructionContext>(C)));
206207
Data2.setPointer(const_cast<ConstructionContext *>(C));
207208
}
208209

clang/include/clang/Analysis/ConstructionContext.h

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,14 @@ class ConstructionContextItem {
3636
ElidedDestructorKind,
3737
ElidableConstructorKind,
3838
ArgumentKind,
39-
STATEMENT_WITH_INDEX_KIND_BEGIN=ArgumentKind,
40-
STATEMENT_WITH_INDEX_KIND_END=ArgumentKind,
39+
LambdaCaptureKind,
40+
STATEMENT_WITH_INDEX_KIND_BEGIN = ArgumentKind,
41+
STATEMENT_WITH_INDEX_KIND_END = LambdaCaptureKind,
4142
STATEMENT_KIND_BEGIN = VariableKind,
42-
STATEMENT_KIND_END = ArgumentKind,
43+
STATEMENT_KIND_END = LambdaCaptureKind,
4344
InitializerKind,
44-
INITIALIZER_KIND_BEGIN=InitializerKind,
45-
INITIALIZER_KIND_END=InitializerKind
45+
INITIALIZER_KIND_BEGIN = InitializerKind,
46+
INITIALIZER_KIND_END = InitializerKind
4647
};
4748

4849
LLVM_DUMP_METHOD static StringRef getKindAsString(ItemKind K) {
@@ -55,6 +56,8 @@ class ConstructionContextItem {
5556
case ElidedDestructorKind: return "elide destructor";
5657
case ElidableConstructorKind: return "elide constructor";
5758
case ArgumentKind: return "construct into argument";
59+
case LambdaCaptureKind:
60+
return "construct into lambda captured variable";
5861
case InitializerKind: return "construct into member variable";
5962
};
6063
llvm_unreachable("Unknown ItemKind");
@@ -72,7 +75,7 @@ class ConstructionContextItem {
7275

7376
bool hasIndex() const {
7477
return Kind >= STATEMENT_WITH_INDEX_KIND_BEGIN &&
75-
Kind >= STATEMENT_WITH_INDEX_KIND_END;
78+
Kind <= STATEMENT_WITH_INDEX_KIND_END;
7679
}
7780

7881
bool hasInitializer() const {
@@ -127,6 +130,9 @@ class ConstructionContextItem {
127130
ConstructionContextItem(const CXXCtorInitializer *Init)
128131
: Data(Init), Kind(InitializerKind), Index(0) {}
129132

133+
ConstructionContextItem(const LambdaExpr *LE, unsigned Index)
134+
: Data(LE), Kind(LambdaCaptureKind), Index(Index) {}
135+
130136
ItemKind getKind() const { return Kind; }
131137

132138
LLVM_DUMP_METHOD StringRef getKindAsString() const {
@@ -254,7 +260,8 @@ class ConstructionContext {
254260
CXX17ElidedCopyReturnedValueKind,
255261
RETURNED_VALUE_BEGIN = SimpleReturnedValueKind,
256262
RETURNED_VALUE_END = CXX17ElidedCopyReturnedValueKind,
257-
ArgumentKind
263+
ArgumentKind,
264+
LambdaCaptureKind
258265
};
259266

260267
protected:
@@ -674,6 +681,42 @@ class ArgumentConstructionContext : public ConstructionContext {
674681
}
675682
};
676683

684+
class LambdaCaptureConstructionContext : public ConstructionContext {
685+
// The lambda of which the initializer we capture.
686+
const LambdaExpr *LE;
687+
688+
// Index of the captured element in the captured list.
689+
unsigned Index;
690+
691+
friend class ConstructionContext; // Allows to create<>() itself.
692+
693+
explicit LambdaCaptureConstructionContext(const LambdaExpr *LE,
694+
unsigned Index)
695+
: ConstructionContext(LambdaCaptureKind), LE(LE), Index(Index) {}
696+
697+
public:
698+
const LambdaExpr *getLambdaExpr() const { return LE; }
699+
unsigned getIndex() const { return Index; }
700+
701+
const Expr *getInitializer() const {
702+
return *(LE->capture_init_begin() + Index);
703+
}
704+
705+
const FieldDecl *getFieldDecl() const {
706+
auto It = LE->getLambdaClass()->field_begin();
707+
std::advance(It, Index);
708+
return *It;
709+
}
710+
711+
const ArrayInitLoopExpr *getArrayInitLoop() const override {
712+
return dyn_cast_or_null<ArrayInitLoopExpr>(getInitializer());
713+
}
714+
715+
static bool classof(const ConstructionContext *CC) {
716+
return CC->getKind() == LambdaCaptureKind;
717+
}
718+
};
719+
677720
} // end namespace clang
678721

679722
#endif // LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H

clang/lib/Analysis/CFG.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3348,9 +3348,20 @@ CFGBlock *CFGBuilder::VisitBlockExpr(BlockExpr *E, AddStmtChoice asc) {
33483348

33493349
CFGBlock *CFGBuilder::VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc) {
33503350
CFGBlock *LastBlock = VisitNoRecurse(E, asc);
3351+
3352+
unsigned Idx = 0;
33513353
for (LambdaExpr::capture_init_iterator it = E->capture_init_begin(),
3352-
et = E->capture_init_end(); it != et; ++it) {
3354+
et = E->capture_init_end();
3355+
it != et; ++it, ++Idx) {
33533356
if (Expr *Init = *it) {
3357+
// If the initializer is an ArrayInitLoopExpr, we want to extract the
3358+
// initializer, that's used for each element.
3359+
const auto *AILE = dyn_cast_or_null<ArrayInitLoopExpr>(Init);
3360+
3361+
findConstructionContexts(ConstructionContextLayer::create(
3362+
cfg->getBumpVectorContext(), {E, Idx}),
3363+
AILE ? AILE->getSubExpr() : Init);
3364+
33543365
CFGBlock *Tmp = Visit(Init);
33553366
if (Tmp)
33563367
LastBlock = Tmp;
@@ -5624,6 +5635,12 @@ static void print_construction_context(raw_ostream &OS,
56245635
Stmts.push_back(TOCC->getConstructorAfterElision());
56255636
break;
56265637
}
5638+
case ConstructionContext::LambdaCaptureKind: {
5639+
const auto *LCC = cast<LambdaCaptureConstructionContext>(CC);
5640+
Helper.handledStmt(const_cast<LambdaExpr *>(LCC->getLambdaExpr()), OS);
5641+
OS << "+" << LCC->getIndex();
5642+
return;
5643+
}
56275644
case ConstructionContext::ArgumentKind: {
56285645
const auto *ACC = cast<ArgumentConstructionContext>(CC);
56295646
if (const Stmt *BTE = ACC->getCXXBindTemporaryExpr()) {

clang/lib/Analysis/ConstructionContext.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,12 @@ const ConstructionContext *ConstructionContext::createBoundTemporaryFromLayers(
156156
return create<CXX17ElidedCopyConstructorInitializerConstructionContext>(
157157
C, I, BTE);
158158
}
159+
case ConstructionContextItem::LambdaCaptureKind: {
160+
assert(ParentLayer->isLast());
161+
const auto *E = cast<LambdaExpr>(ParentItem.getStmt());
162+
return create<LambdaCaptureConstructionContext>(C, E,
163+
ParentItem.getIndex());
164+
}
159165
} // switch (ParentItem.getKind())
160166

161167
llvm_unreachable("Unexpected construction context with destructor!");
@@ -200,6 +206,11 @@ const ConstructionContext *ConstructionContext::createFromLayers(
200206
case ConstructionContextItem::ElidableConstructorKind: {
201207
llvm_unreachable("The argument needs to be materialized first!");
202208
}
209+
case ConstructionContextItem::LambdaCaptureKind: {
210+
assert(TopLayer->isLast());
211+
const auto *E = cast<LambdaExpr>(TopItem.getStmt());
212+
return create<LambdaCaptureConstructionContext>(C, E, TopItem.getIndex());
213+
}
203214
case ConstructionContextItem::InitializerKind: {
204215
assert(TopLayer->isLast());
205216
const CXXCtorInitializer *I = TopItem.getCXXCtorInitializer();

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,9 @@ ExprEngine::addObjectUnderConstruction(ProgramStateRef State,
530530
Init = VD->getInit();
531531
}
532532

533+
if (auto LE = dyn_cast_or_null<LambdaExpr>(Item.getStmtOrNull()))
534+
Init = *(LE->capture_init_begin() + Item.getIndex());
535+
533536
if (!Init && !Item.getStmtOrNull())
534537
Init = Item.getCXXCtorInitializer()->getInit();
535538

clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,23 @@ SVal ExprEngine::computeObjectUnderConstruction(
290290

291291
return loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
292292
}
293+
case ConstructionContext::LambdaCaptureKind: {
294+
CallOpts.IsTemporaryCtorOrDtor = true;
295+
296+
const auto *LCC = cast<LambdaCaptureConstructionContext>(CC);
297+
298+
SVal Base = loc::MemRegionVal(
299+
MRMgr.getCXXTempObjectRegion(LCC->getInitializer(), LCtx));
300+
301+
const auto *CE = dyn_cast_or_null<CXXConstructExpr>(E);
302+
if (getIndexOfElementToConstruct(State, CE, LCtx)) {
303+
CallOpts.IsArrayCtorOrDtor = true;
304+
Base = State->getLValue(E->getType(), svalBuilder.makeArrayIndex(Idx),
305+
Base);
306+
}
307+
308+
return Base;
309+
}
293310
case ConstructionContext::ArgumentKind: {
294311
// Arguments are technically temporaries.
295312
CallOpts.IsTemporaryCtorOrDtor = true;
@@ -450,6 +467,17 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction(
450467

451468
return State;
452469
}
470+
case ConstructionContext::LambdaCaptureKind: {
471+
const auto *LCC = cast<LambdaCaptureConstructionContext>(CC);
472+
473+
// If we capture and array, we want to store the super region, not a
474+
// sub-region.
475+
if (const auto *EL = dyn_cast_or_null<ElementRegion>(V.getAsRegion()))
476+
V = loc::MemRegionVal(EL->getSuperRegion());
477+
478+
return addObjectUnderConstruction(
479+
State, {LCC->getLambdaExpr(), LCC->getIndex()}, LCtx, V);
480+
}
453481
case ConstructionContext::ArgumentKind: {
454482
const auto *ACC = cast<ArgumentConstructionContext>(CC);
455483
if (const auto *BTE = ACC->getCXXBindTemporaryExpr())
@@ -1105,19 +1133,40 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred,
11051133

11061134
// If we created a new MemRegion for the lambda, we should explicitly bind
11071135
// the captures.
1136+
unsigned Idx = 0;
11081137
CXXRecordDecl::field_iterator CurField = LE->getLambdaClass()->field_begin();
11091138
for (LambdaExpr::const_capture_init_iterator i = LE->capture_init_begin(),
11101139
e = LE->capture_init_end();
1111-
i != e; ++i, ++CurField) {
1140+
i != e; ++i, ++CurField, ++Idx) {
11121141
FieldDecl *FieldForCapture = *CurField;
11131142
SVal FieldLoc = State->getLValue(FieldForCapture, V);
11141143

11151144
SVal InitVal;
11161145
if (!FieldForCapture->hasCapturedVLAType()) {
11171146
Expr *InitExpr = *i;
1147+
1148+
if (const auto AILE = dyn_cast<ArrayInitLoopExpr>(InitExpr)) {
1149+
// If the AILE initializes a POD array, we need to keep it as the
1150+
// InitExpr.
1151+
if (dyn_cast<CXXConstructExpr>(AILE->getSubExpr()))
1152+
InitExpr = AILE->getSubExpr();
1153+
}
1154+
11181155
assert(InitExpr && "Capture missing initialization expression");
1119-
InitVal = State->getSVal(InitExpr, LocCtxt);
1156+
1157+
if (dyn_cast<CXXConstructExpr>(InitExpr)) {
1158+
InitVal = *getObjectUnderConstruction(State, {LE, Idx}, LocCtxt);
1159+
InitVal = State->getSVal(InitVal.getAsRegion());
1160+
1161+
State = finishObjectConstruction(State, {LE, Idx}, LocCtxt);
1162+
} else
1163+
InitVal = State->getSVal(InitExpr, LocCtxt);
1164+
11201165
} else {
1166+
1167+
assert(!getObjectUnderConstruction(State, {LE, Idx}, LocCtxt) &&
1168+
"VLA capture by value is a compile time error!");
1169+
11211170
// The field stores the length of a captured variable-length array.
11221171
// These captures don't have initialization expressions; instead we
11231172
// get the length from the VLAType size expression.

clang/test/Analysis/array-init-loop.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ struct S3 {
160160
int i;
161161
};
162162

163+
// The duplicate is required to emit a warning at 2 different places.
164+
struct S3_duplicate {
165+
int i;
166+
};
167+
163168
void array_uninit_non_pod() {
164169
S3 arr[1];
165170

@@ -170,24 +175,23 @@ void lambda_init_non_pod() {
170175
S2::c = 0;
171176
S2 arr[4];
172177

173-
// FIXME: These should be TRUE, but we fail to capture the array properly.
174178
auto l = [arr] { return arr[0].i; }();
175-
clang_analyzer_eval(l == 2); // expected-warning{{TRUE}} // expected-warning{{FALSE}}
179+
clang_analyzer_eval(l == 2); // expected-warning{{TRUE}}
176180

177181
l = [arr] { return arr[1].i; }();
178-
clang_analyzer_eval(l == 3); // expected-warning{{TRUE}} // expected-warning{{FALSE}}
182+
clang_analyzer_eval(l == 3); // expected-warning{{TRUE}}
179183

180184
l = [arr] { return arr[2].i; }();
181-
clang_analyzer_eval(l == 4); // expected-warning{{TRUE}} // expected-warning{{FALSE}}
185+
clang_analyzer_eval(l == 4); // expected-warning{{TRUE}}
182186

183187
l = [arr] { return arr[3].i; }();
184-
clang_analyzer_eval(l == 5); // expected-warning{{TRUE}} // expected-warning{{FALSE}}
188+
clang_analyzer_eval(l == 5); // expected-warning{{TRUE}}
185189
}
186190

187191
void lambda_uninit_non_pod() {
188-
S3 arr[4];
192+
S3_duplicate arr[4];
189193

190-
int l = [arr] { return arr[3].i; }();
194+
int l = [arr] { return arr[3].i; }(); // expected-warning@164{{ in implicit constructor is garbage or undefined }}
191195
}
192196

193197
// If this struct is being copy/move constructed by the implicit ctors, ArrayInitLoopExpr

clang/test/Analysis/lambdas.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,12 +400,11 @@ int f() {
400400
// CHECK: [B1]
401401
// CHECK: 1: x
402402
// CHECK: 2: [B1.1] (ImplicitCastExpr, NoOp, const struct X)
403-
// CHECK: 3: [B1.2] (CXXConstructExpr, struct X)
403+
// CHECK: 3: [B1.2] (CXXConstructExpr[B1.4]+0, struct X)
404404
// CHECK: 4: [x] {
405405
// CHECK: }
406406
// CHECK: 5: (void)[B1.4] (CStyleCastExpr, ToVoid, void)
407407
// CHECK: Preds (1): B2
408408
// CHECK: Succs (1): B0
409409
// CHECK: [B0 (EXIT)]
410410
// CHECK: Preds (1): B1
411-

0 commit comments

Comments
 (0)