r334678 - [analyzer] NFC: Merge code for finding and tracking construction target.
Artem Dergachev via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 13 18:20:13 PDT 2018
Author: dergachev
Date: Wed Jun 13 18:20:12 2018
New Revision: 334678
URL: http://llvm.org/viewvc/llvm-project?rev=334678&view=rev
Log:
[analyzer] NFC: Merge code for finding and tracking construction target.
When analyzing C++ code, a common operation in the analyzer is to discover
target region for object construction by looking at CFG metadata ("construction
contexts"), and then track the region path-sensitively until object construction
is resolved, where the amount of information, again, depends on construction
context.
Scan construction context only once for both purposes.
Differential Revision: https://reviews.llvm.org/D47304
Modified:
cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h?rev=334678&r1=334677&r2=334678&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h Wed Jun 13 18:20:12 2018
@@ -744,14 +744,15 @@ private:
/// constructing into an existing region.
const CXXConstructExpr *findDirectConstructorForCurrentCFGElement();
- /// For a given constructor, look forward in the current CFG block to
- /// determine the region into which an object will be constructed by \p CE.
- /// When the lookahead fails, a temporary region is returned, and the
+ /// Update the program state with all the path-sensitive information
+ /// that's necessary to perform construction of an object with a given
+ /// syntactic construction context. If the construction context is unavailable
+ /// or unusable for any reason, a dummy temporary region is returned, and the
/// IsConstructorWithImproperlyModeledTargetRegion flag is set in \p CallOpts.
- SVal getLocationForConstructedObject(const CXXConstructExpr *CE,
- ExplodedNode *Pred,
- const ConstructionContext *CC,
- EvalCallOptions &CallOpts);
+ /// Returns the updated program state and the new object's this-region.
+ std::pair<ProgramStateRef, SVal> prepareForObjectConstruction(
+ const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
+ const ConstructionContext *CC, EvalCallOptions &CallOpts);
/// Store the location of a C++ object corresponding to a statement
/// until the statement is actually encountered. For example, if a DeclStmt
@@ -782,14 +783,6 @@ private:
static bool areAllObjectsFullyConstructed(ProgramStateRef State,
const LocationContext *FromLC,
const LocationContext *ToLC);
-
- /// Adds an initialized temporary and/or a materialization, whichever is
- /// necessary, by looking at the whole construction context. Handles
- /// function return values, which need the construction context of the parent
- /// stack frame, automagically.
- ProgramStateRef markStatementsCorrespondingToConstructedObject(
- ProgramStateRef State, const ConstructionContext *CC,
- const LocationContext *LC, SVal V);
};
/// Traits for storing the call processing policy inside GDM.
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp?rev=334678&r1=334677&r2=334678&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp Wed Jun 13 18:20:12 2018
@@ -417,81 +417,6 @@ bool ExprEngine::areAllObjectsFullyConst
return true;
}
-ProgramStateRef ExprEngine::markStatementsCorrespondingToConstructedObject(
- ProgramStateRef State, const ConstructionContext *CC,
- const LocationContext *LC, SVal V) {
- if (CC) {
- // If the temporary is being returned from the function, it will be
- // destroyed or lifetime-extended in the caller stack frame.
- if (isa<ReturnedValueConstructionContext>(CC)) {
- const StackFrameContext *SFC = LC->getCurrentStackFrame();
- assert(SFC);
- LC = SFC->getParent();
- if (!LC) {
- // We are on the top frame. We won't ever need any info
- // for this temporary, so don't set anything.
- return State;
- }
- const CFGElement &CallElem =
- (*SFC->getCallSiteBlock())[SFC->getIndex()];
- auto RTCElem = CallElem.getAs<CFGCXXRecordTypedCall>();
- if (!RTCElem) {
- // We have a parent stack frame, but no construction context for the
- // return value. Give up until we provide the construction context
- // at the call site.
- return State;
- }
- // We use the ReturnedValueConstructionContext as an indication that we
- // need to look for the actual construction context on the parent stack
- // frame. This purpose has been fulfilled, so now we replace CC with the
- // actual construction context.
- CC = RTCElem->getConstructionContext();
- if (!isa<TemporaryObjectConstructionContext>(CC)) {
- // TODO: We are not returning an object into a temporary. There must
- // be copy elision happening at the call site. We still need to
- // explicitly support the situation when the return value is put
- // into another return statement, i.e.
- // ReturnedValueConstructionContexts are chained through multiple
- // stack frames before finally settling in a temporary.
- // We don't seem to need to explicitly support construction into
- // a variable after a return.
- return State;
- }
- // Proceed to deal with the temporary we've found on the parent
- // stack frame.
- }
- // In case of temporary object construction, extract data necessary for
- // destruction and lifetime extension.
- if (const auto *TCC = dyn_cast<TemporaryObjectConstructionContext>(CC)) {
- if (AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG()) {
- const CXXBindTemporaryExpr *BTE =
- TCC->getCXXBindTemporaryExpr();
- const MaterializeTemporaryExpr *MTE =
- TCC->getMaterializedTemporaryExpr();
- if (!BTE) {
- // FIXME: Lifetime extension for temporaries without destructors
- // is not implemented yet.
- MTE = nullptr;
- }
- if (MTE && MTE->getStorageDuration() != SD_FullExpression) {
- // If the temporary is lifetime-extended, don't save the BTE,
- // because we don't need a temporary destructor, but an automatic
- // destructor.
- BTE = nullptr;
- }
-
- if (BTE)
- State = addObjectUnderConstruction(State, BTE, LC, V);
-
- if (MTE)
- State = addObjectUnderConstruction(State, MTE, LC, V);
- }
- }
- }
-
- return State;
-}
-
//===----------------------------------------------------------------------===//
// Top-level transfer function logic (Dispatcher).
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp?rev=334678&r1=334677&r2=334678&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp Wed Jun 13 18:20:12 2018
@@ -110,13 +110,9 @@ SVal ExprEngine::makeZeroElementRegion(P
return LValue;
}
-
-SVal ExprEngine::getLocationForConstructedObject(const CXXConstructExpr *CE,
- ExplodedNode *Pred,
- const ConstructionContext *CC,
- EvalCallOptions &CallOpts) {
- const LocationContext *LCtx = Pred->getLocationContext();
- ProgramStateRef State = Pred->getState();
+std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
+ const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
+ const ConstructionContext *CC, EvalCallOptions &CallOpts) {
MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
// See if we're constructing an existing region by looking at the
@@ -129,8 +125,9 @@ SVal ExprEngine::getLocationForConstruct
const auto *Var = cast<VarDecl>(DS->getSingleDecl());
SVal LValue = State->getLValue(Var, LCtx);
QualType Ty = Var->getType();
- return makeZeroElementRegion(State, LValue, Ty,
- CallOpts.IsArrayCtorOrDtor);
+ return std::make_pair(
+ State,
+ makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor));
}
case ConstructionContext::SimpleConstructorInitializerKind: {
const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
@@ -154,7 +151,7 @@ SVal ExprEngine::getLocationForConstruct
QualType Ty = Field->getType();
FieldVal = makeZeroElementRegion(State, FieldVal, Ty,
CallOpts.IsArrayCtorOrDtor);
- return FieldVal;
+ return std::make_pair(State, FieldVal);
}
case ConstructionContext::NewAllocatedObjectKind: {
if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) {
@@ -167,44 +164,21 @@ SVal ExprEngine::getLocationForConstruct
// TODO: In fact, we need to call the constructor for every
// allocated element, not just the first one!
CallOpts.IsArrayCtorOrDtor = true;
- return loc::MemRegionVal(getStoreManager().GetElementZeroRegion(
- MR, NE->getType()->getPointeeType()));
+ return std::make_pair(
+ State, loc::MemRegionVal(getStoreManager().GetElementZeroRegion(
+ MR, NE->getType()->getPointeeType())));
}
- return V;
+ return std::make_pair(State, V);
}
// TODO: Detect when the allocator returns a null pointer.
// Constructor shall not be called in this case.
}
break;
}
- case ConstructionContext::TemporaryObjectKind: {
- const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
- if (const auto *MTE = TOCC->getMaterializedTemporaryExpr()) {
- if (const ValueDecl *VD = MTE->getExtendingDecl()) {
- assert(MTE->getStorageDuration() != SD_FullExpression);
- if (!VD->getType()->isReferenceType()) {
- // We're lifetime-extended by a surrounding aggregate.
- // Automatic destructors aren't quite working in this case
- // on the CFG side. We should warn the caller about that.
- // FIXME: Is there a better way to retrieve this information from
- // the MaterializeTemporaryExpr?
- CallOpts.IsTemporaryLifetimeExtendedViaAggregate = true;
- }
- }
- }
- // TODO: Support temporaries lifetime-extended via static references.
- // They'd need a getCXXStaticTempObjectRegion().
- CallOpts.IsTemporaryCtorOrDtor = true;
- return loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(CE, LCtx));
- }
case ConstructionContext::SimpleReturnedValueKind: {
// The temporary is to be managed by the parent stack frame.
// So build it in the parent stack frame if we're not in the
// top frame of the analysis.
- // TODO: What exactly happens when we are? Does the temporary object live
- // long enough in the region store in this case? Would checkers think
- // that this object immediately goes out of scope?
- const LocationContext *TempLCtx = LCtx;
const StackFrameContext *SFC = LCtx->getCurrentStackFrame();
if (const LocationContext *CallerLCtx = SFC->getParent()) {
auto RTC = (*SFC->getCallSiteBlock())[SFC->getIndex()]
@@ -213,19 +187,74 @@ SVal ExprEngine::getLocationForConstruct
// We were unable to find the correct construction context for the
// call in the parent stack frame. This is equivalent to not being
// able to find construction context at all.
- CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
+ break;
} else if (!isa<TemporaryObjectConstructionContext>(
RTC->getConstructionContext())) {
// FIXME: The return value is constructed directly into a
// non-temporary due to C++17 mandatory copy elision. This is not
// implemented yet.
assert(getContext().getLangOpts().CPlusPlus17);
- CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
+ break;
}
- TempLCtx = CallerLCtx;
+ CC = RTC->getConstructionContext();
+ LCtx = CallerLCtx;
+ } else {
+ // We are on the top frame of the analysis.
+ // TODO: What exactly happens when we are? Does the temporary object
+ // live long enough in the region store in this case? Would checkers
+ // think that this object immediately goes out of scope?
+ CallOpts.IsTemporaryCtorOrDtor = true;
+ SVal V = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
+ return std::make_pair(State, V);
}
+
+ // Continue as if we have a temporary with a different location context.
+ // FALLTHROUGH.
+ }
+ case ConstructionContext::TemporaryObjectKind: {
+ const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
+ const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();
+ const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();
+
+ if (!BTE) {
+ // FIXME: Lifetime extension for temporaries without destructors
+ // is not implemented yet.
+ MTE = nullptr;
+ }
+
+ if (MTE) {
+ if (const ValueDecl *VD = MTE->getExtendingDecl()) {
+ assert(MTE->getStorageDuration() != SD_FullExpression);
+ if (!VD->getType()->isReferenceType()) {
+ // We're lifetime-extended by a surrounding aggregate.
+ // Automatic destructors aren't quite working in this case
+ // on the CFG side. We should warn the caller about that.
+ // FIXME: Is there a better way to retrieve this information from
+ // the MaterializeTemporaryExpr?
+ CallOpts.IsTemporaryLifetimeExtendedViaAggregate = true;
+ }
+ }
+ }
+
+ if (MTE && MTE->getStorageDuration() != SD_FullExpression) {
+ // If the temporary is lifetime-extended, don't save the BTE,
+ // because we don't need a temporary destructor, but an automatic
+ // destructor.
+ BTE = nullptr;
+ }
+
+ // FIXME: Support temporaries lifetime-extended via static references.
+ // They'd need a getCXXStaticTempObjectRegion().
+ SVal V = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
+
+ if (BTE)
+ State = addObjectUnderConstruction(State, BTE, LCtx, V);
+
+ if (MTE)
+ State = addObjectUnderConstruction(State, MTE, LCtx, V);
+
CallOpts.IsTemporaryCtorOrDtor = true;
- return loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(CE, TempLCtx));
+ return std::make_pair(State, V);
}
case ConstructionContext::CXX17ElidedCopyVariableKind:
case ConstructionContext::CXX17ElidedCopyReturnedValueKind:
@@ -237,7 +266,8 @@ SVal ExprEngine::getLocationForConstruct
// If we couldn't find an existing region to construct into, assume we're
// constructing a temporary. Notify the caller of our failure.
CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
- return loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(CE, LCtx));
+ return std::make_pair(
+ State, loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx)));
}
const CXXConstructExpr *
@@ -288,7 +318,8 @@ void ExprEngine::VisitCXXConstructExpr(c
switch (CE->getConstructionKind()) {
case CXXConstructExpr::CK_Complete: {
- Target = getLocationForConstructedObject(CE, Pred, CC, CallOpts);
+ std::tie(State, Target) =
+ prepareForObjectConstruction(CE, State, LCtx, CC, CallOpts);
break;
}
case CXXConstructExpr::CK_VirtualBase:
@@ -349,6 +380,18 @@ void ExprEngine::VisitCXXConstructExpr(c
}
}
+ if (State != Pred->getState()) {
+ static SimpleProgramPointTag T("ExprEngine",
+ "Prepare for object construction");
+ ExplodedNodeSet DstPrepare;
+ StmtNodeBuilder BldrPrepare(Pred, DstPrepare, *currBldrCtx);
+ BldrPrepare.generateNode(CE, Pred, State, &T, ProgramPoint::PreStmtKind);
+ assert(DstPrepare.size() <= 1);
+ if (DstPrepare.size() == 0)
+ return;
+ Pred = *BldrPrepare.begin();
+ }
+
CallEventManager &CEMgr = getStateManager().getCallEventManager();
CallEventRef<CXXConstructorCall> Call =
CEMgr.getCXXConstructorCall(CE, Target.getAsRegion(), State, LCtx);
@@ -380,9 +423,6 @@ void ExprEngine::VisitCXXConstructExpr(c
State = State->bindDefaultZero(Target, LCtx);
}
- State = markStatementsCorrespondingToConstructedObject(State, CC, LCtx,
- Target);
-
Bldr.generateNode(CE, *I, State, /*tag=*/nullptr,
ProgramPoint::PreStmtKind);
}
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp?rev=334678&r1=334677&r2=334678&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp Wed Jun 13 18:20:12 2018
@@ -587,18 +587,20 @@ ProgramStateRef ExprEngine::bindReturnVa
unsigned Count = currBldrCtx->blockCount();
if (auto RTC = getCurrentCFGElement().getAs<CFGCXXRecordTypedCall>()) {
// Conjure a temporary if the function returns an object by value.
- MemRegionManager &MRMgr = svalBuilder.getRegionManager();
- const CXXTempObjectRegion *TR = MRMgr.getCXXTempObjectRegion(E, LCtx);
- State = markStatementsCorrespondingToConstructedObject(
- State, RTC->getConstructionContext(), LCtx, loc::MemRegionVal(TR));
-
+ SVal Target;
+ assert(RTC->getStmt() == Call.getOriginExpr());
+ EvalCallOptions CallOpts; // FIXME: We won't really need those.
+ std::tie(State, Target) =
+ prepareForObjectConstruction(Call.getOriginExpr(), State, LCtx,
+ RTC->getConstructionContext(), CallOpts);
+ assert(Target.getAsRegion());
// Invalidate the region so that it didn't look uninitialized. Don't notify
// the checkers.
- State = State->invalidateRegions(TR, E, Count, LCtx,
+ State = State->invalidateRegions(Target.getAsRegion(), E, Count, LCtx,
/* CausedByPointerEscape=*/false, nullptr,
&Call, nullptr);
- R = State->getSVal(TR, E->getType());
+ R = State->getSVal(Target.castAs<Loc>(), E->getType());
} else {
// Conjure a symbol if the return value is unknown.
More information about the cfe-commits
mailing list