Skip to content

Commit 1e3dbd7

Browse files
committed
[analyzer] Track temporaries without construction contexts for destruction.
Sometimes it is not known at compile time which temporary objects will be constructed, eg. 'x ? A() : B()' or 'C() || D()'. In this case we track which temporary was constructed to know how to properly call the destructor. Once the construction context for temporaries was introduced, we moved the tracking code to the code that investigates the construction context. Bring back the old mechanism because construction contexts are not always available yet - eg. in the case where a temporary is constructed without a constructor expression, eg. returned from a function by value. The mechanism should still go away eventually. Additionally, fix a bug in the temporary cleanup code for the case when construction contexts are not available, which could lead to temporaries staying in the program state and increasing memory consumption. Differential Revision: https://reviews.llvm.org/D43666 llvm-svn: 326246
1 parent f01831e commit 1e3dbd7

File tree

3 files changed

+59
-17
lines changed

3 files changed

+59
-17
lines changed

clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,10 @@ class ExprEngine : public SubEngine {
452452
ExplodedNode *Pred,
453453
ExplodedNodeSet &Dst);
454454

455+
void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE,
456+
ExplodedNodeSet &PreVisit,
457+
ExplodedNodeSet &Dst);
458+
455459
void VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred,
456460
ExplodedNodeSet &Dst);
457461

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,34 @@ void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
10891089
}
10901090
}
10911091

1092+
void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE,
1093+
ExplodedNodeSet &PreVisit,
1094+
ExplodedNodeSet &Dst) {
1095+
// This is a fallback solution in case we didn't have a construction
1096+
// context when we were constructing the temporary. Otherwise the map should
1097+
// have been populated there.
1098+
if (!getAnalysisManager().options.includeTemporaryDtorsInCFG()) {
1099+
// In case we don't have temporary destructors in the CFG, do not mark
1100+
// the initialization - we would otherwise never clean it up.
1101+
Dst = PreVisit;
1102+
return;
1103+
}
1104+
StmtNodeBuilder StmtBldr(PreVisit, Dst, *currBldrCtx);
1105+
for (ExplodedNode *Node : PreVisit) {
1106+
ProgramStateRef State = Node->getState();
1107+
const auto &Key = std::make_pair(BTE, Node->getStackFrame());
1108+
1109+
if (!State->contains<InitializedTemporaries>(Key)) {
1110+
// FIXME: Currently the state might also already contain the marker due to
1111+
// incorrect handling of temporaries bound to default parameters; for
1112+
// those, we currently skip the CXXBindTemporaryExpr but rely on adding
1113+
// temporary destructor nodes.
1114+
State = State->set<InitializedTemporaries>(Key, nullptr);
1115+
}
1116+
StmtBldr.generateNode(BTE, Node, State);
1117+
}
1118+
}
1119+
10921120
namespace {
10931121
class CollectReachableSymbolsCallback final : public SymbolVisitor {
10941122
InvalidatedSymbols Symbols;
@@ -1251,7 +1279,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
12511279
Bldr.takeNodes(Pred);
12521280
ExplodedNodeSet PreVisit;
12531281
getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
1254-
getCheckerManager().runCheckersForPostStmt(Dst, PreVisit, S, *this);
1282+
ExplodedNodeSet Next;
1283+
VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), PreVisit, Next);
1284+
getCheckerManager().runCheckersForPostStmt(Dst, Next, S, *this);
12551285
Bldr.addNodes(Dst);
12561286
break;
12571287
}
@@ -2194,13 +2224,13 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
21942224
Pred->getLocationContext(),
21952225
Pred->getStackFrame()->getParent()));
21962226

2197-
// FIXME: We currently assert that temporaries are clear, as lifetime extended
2198-
// temporaries are not always modelled correctly. In some cases when we
2199-
// materialize the temporary, we do createTemporaryRegionIfNeeded(), and
2200-
// the region changes, and also the respective destructor becomes automatic
2201-
// from temporary. So for now clean up the state manually before asserting.
2202-
// Ideally, the code above the assertion should go away, but the assertion
2203-
// should remain.
2227+
// FIXME: We currently cannot assert that temporaries are clear, because
2228+
// lifetime extended temporaries are not always modelled correctly. In some
2229+
// cases when we materialize the temporary, we do
2230+
// createTemporaryRegionIfNeeded(), and the region changes, and also the
2231+
// respective destructor becomes automatic from temporary. So for now clean up
2232+
// the state manually before asserting. Ideally, the code above the assertion
2233+
// should go away, but the assertion should remain.
22042234
{
22052235
ExplodedNodeSet CleanUpTemporaries;
22062236
NodeBuilder Bldr(Pred, CleanUpTemporaries, BC);
@@ -2217,9 +2247,12 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
22172247
LC = LC->getParent();
22182248
}
22192249
if (State != Pred->getState()) {
2220-
Bldr.generateNode(Pred->getLocation(), State, Pred);
2221-
assert(CleanUpTemporaries.size() <= 1);
2222-
Pred = CleanUpTemporaries.empty() ? Pred : *CleanUpTemporaries.begin();
2250+
Pred = Bldr.generateNode(Pred->getLocation(), State, Pred);
2251+
if (!Pred) {
2252+
// The node with clean temporaries already exists. We might have reached
2253+
// it on a path on which we initialize different temporaries.
2254+
return;
2255+
}
22232256
}
22242257
}
22252258
assert(areInitializedTemporariesClear(Pred->getState(),

clang/test/Analysis/temporaries.cpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,13 @@ int glob;
900900

901901
class C {
902902
public:
903-
~C() { glob = 1; }
903+
~C() {
904+
glob = 1;
905+
clang_analyzer_checkInlined(true);
906+
#ifdef TEMPORARY_DTORS
907+
// expected-warning@-2{{TRUE}}
908+
#endif
909+
}
904910
};
905911

906912
C get();
@@ -913,12 +919,11 @@ void test(int coin) {
913919
// temporaries: the return value of get() and the elidable copy constructor
914920
// of that return value into is(). According to the CFG, we need to cleanup
915921
// both of them depending on whether the temporary corresponding to the
916-
// return value of get() was initialized. However, for now we don't track
917-
// temporaries returned from functions, so we take the wrong branch.
922+
// return value of get() was initialized. However, we didn't track
923+
// temporaries returned from functions, so we took the wrong branch.
918924
coin && is(get()); // no-crash
919-
// FIXME: We should have called the destructor, i.e. should be TRUE,
920-
// at least when we inline temporary destructors.
921-
clang_analyzer_eval(glob == 1); // expected-warning{{UNKNOWN}}
925+
// FIXME: Should be true once we inline all destructors.
926+
clang_analyzer_eval(glob); // expected-warning{{UNKNOWN}}
922927
}
923928
} // namespace dont_forget_destructor_around_logical_op
924929

0 commit comments

Comments
 (0)