[cfe-commits] r159608 - in /cfe/trunk: include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp lib/StaticAnalyzer/Core/ExprEngineCXX.cpp lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp test/Analysis/new.cpp
Jordan Rose
jordan_rose at apple.com
Mon Jul 2 15:21:47 PDT 2012
Author: jrose
Date: Mon Jul 2 17:21:47 2012
New Revision: 159608
URL: http://llvm.org/viewvc/llvm-project?rev=159608&view=rev
Log:
[analyzer] Introduce CXXAllocatorCall to handle placement arg invalidation.
This is NOT full-blown support for operator new, but removes some nasty
duplicated code introduced in r158784.
Modified:
cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h
cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
cfe/trunk/test/Analysis/new.cpp
Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h?rev=159608&r1=159607&r2=159608&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h Mon Jul 2 17:21:47 2012
@@ -33,8 +33,9 @@
CE_BEG_SIMPLE_CALLS = CE_Function,
CE_END_SIMPLE_CALLS = CE_Block,
CE_CXXConstructor,
+ CE_CXXAllocator,
CE_BEG_FUNCTION_CALLS = CE_Function,
- CE_END_FUNCTION_CALLS = CE_CXXConstructor,
+ CE_END_FUNCTION_CALLS = CE_CXXAllocator,
CE_ObjCMessage,
CE_ObjCPropertyAccess,
CE_BEG_OBJC_CALLS = CE_ObjCMessage,
@@ -337,6 +338,35 @@
}
};
+class CXXAllocatorCall : public AnyFunctionCall {
+ const CXXNewExpr *E;
+
+public:
+ CXXAllocatorCall(const CXXNewExpr *e, ProgramStateRef St,
+ const LocationContext *LCtx)
+ : AnyFunctionCall(St, LCtx, CE_CXXAllocator), E(e) {}
+
+ const CXXNewExpr *getOriginExpr() const { return E; }
+ SourceRange getSourceRange() const { return E->getSourceRange(); }
+
+ const FunctionDecl *getDecl() const {
+ return E->getOperatorNew();
+ }
+
+ unsigned getNumArgs() const { return E->getNumPlacementArgs() + 1; }
+
+ const Expr *getArgExpr(unsigned Index) const {
+ // The first argument of an allocator call is the size of the allocation.
+ if (Index == 0)
+ return 0;
+ return E->getPlacementArg(Index - 1);
+ }
+
+ static bool classof(const CallEvent *CE) {
+ return CE->getKind() == CE_CXXAllocator;
+ }
+};
+
/// \brief Represents any expression that calls an Objective-C method.
class ObjCMethodCall : public CallEvent {
const ObjCMessageExpr *Msg;
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp?rev=159608&r1=159607&r2=159608&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp Mon Jul 2 17:21:47 2012
@@ -947,6 +947,7 @@
case CE_CXXMember:
case CE_Block:
case CE_CXXConstructor:
+ case CE_CXXAllocator:
// FIXME: These calls are currently unsupported.
return getPersistentStopSummary();
case CE_ObjCMessage:
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp?rev=159608&r1=159607&r2=159608&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp Mon Jul 2 17:21:47 2012
@@ -92,191 +92,51 @@
Bldr.generateNode(PP, Pred, state);
}
-static bool isPointerToConst(const ParmVarDecl *ParamDecl) {
- // FIXME: Copied from ExprEngineCallAndReturn.cpp
- QualType PointeeTy = ParamDecl->getOriginalType()->getPointeeType();
- if (PointeeTy != QualType() && PointeeTy.isConstQualified() &&
- !PointeeTy->isAnyPointerType() && !PointeeTy->isReferenceType()) {
- return true;
- }
- return false;
-}
-
void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ // FIXME: Much of this should eventually migrate to CXXAllocatorCall.
+ // Also, we need to decide how allocators actually work -- they're not
+ // really part of the CXXNewExpr because they happen BEFORE the
+ // CXXConstructExpr subexpression. See PR12014 for some discussion.
StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
unsigned blockCount = currentBuilderContext->getCurrentBlockCount();
const LocationContext *LCtx = Pred->getLocationContext();
DefinedOrUnknownSVal symVal =
- svalBuilder.getConjuredSymbolVal(NULL, CNE, LCtx, CNE->getType(), blockCount);
- const MemRegion *NewReg = cast<loc::MemRegionVal>(symVal).getRegion();
- QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType();
- const ElementRegion *EleReg =
- getStoreManager().GetElementZeroRegion(NewReg, ObjTy);
+ svalBuilder.getConjuredSymbolVal(0, CNE, LCtx, CNE->getType(), blockCount);
ProgramStateRef State = Pred->getState();
+ // Invalidate placement args.
+ CXXAllocatorCall Call(CNE, State, LCtx);
+ State = Call.invalidateRegions(blockCount);
+
if (CNE->isArray()) {
// FIXME: allocating an array requires simulating the constructors.
// For now, just return a symbolicated region.
+ const MemRegion *NewReg = cast<loc::MemRegionVal>(symVal).getRegion();
+ QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType();
+ const ElementRegion *EleReg =
+ getStoreManager().GetElementZeroRegion(NewReg, ObjTy);
State = State->BindExpr(CNE, Pred->getLocationContext(),
loc::MemRegionVal(EleReg));
Bldr.generateNode(CNE, Pred, State);
return;
}
+ // FIXME: Once we have proper support for CXXConstructExprs inside
+ // CXXNewExpr, we need to make sure that the constructed object is not
+ // immediately invalidated here. (The placement call should happen before
+ // the constructor call anyway.)
FunctionDecl *FD = CNE->getOperatorNew();
if (FD && FD->isReservedGlobalPlacementOperator()) {
// Non-array placement new should always return the placement location.
SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx);
State = State->BindExpr(CNE, LCtx, PlacementLoc);
- // FIXME: Once we have proper support for CXXConstructExprs inside
- // CXXNewExpr, we need to make sure that the constructed object is not
- // immediately invalidated here. (The placement call should happen before
- // the constructor call anyway.)
+ } else {
+ State = State->BindExpr(CNE, LCtx, symVal);
}
- // Invalidate placement args.
-
- // FIXME: This is largely copied from invalidateArguments, because
- // CallOrObjCMessage is not general enough to handle new-expressions yet.
- SmallVector<const MemRegion *, 4> RegionsToInvalidate;
-
- unsigned Index = 0;
- for (CXXNewExpr::const_arg_iterator I = CNE->placement_arg_begin(),
- E = CNE->placement_arg_end();
- I != E; ++I) {
- // Pre-increment the argument index to skip over the implicit size arg.
- ++Index;
- if (FD && Index < FD->getNumParams())
- if (isPointerToConst(FD->getParamDecl(Index)))
- continue;
-
- SVal V = State->getSVal(*I, LCtx);
-
- // If we are passing a location wrapped as an integer, unwrap it and
- // invalidate the values referred by the location.
- if (nonloc::LocAsInteger *Wrapped = dyn_cast<nonloc::LocAsInteger>(&V))
- V = Wrapped->getLoc();
- else if (!isa<Loc>(V))
- continue;
-
- if (const MemRegion *R = V.getAsRegion()) {
- // Invalidate the value of the variable passed by reference.
-
- // Are we dealing with an ElementRegion? If the element type is
- // a basic integer type (e.g., char, int) and the underlying region
- // is a variable region then strip off the ElementRegion.
- // FIXME: We really need to think about this for the general case
- // as sometimes we are reasoning about arrays and other times
- // about (char*), etc., is just a form of passing raw bytes.
- // e.g., void *p = alloca(); foo((char*)p);
- if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
- // Checking for 'integral type' is probably too promiscuous, but
- // we'll leave it in for now until we have a systematic way of
- // handling all of these cases. Eventually we need to come up
- // with an interface to StoreManager so that this logic can be
- // appropriately delegated to the respective StoreManagers while
- // still allowing us to do checker-specific logic (e.g.,
- // invalidating reference counts), probably via callbacks.
- if (ER->getElementType()->isIntegralOrEnumerationType()) {
- const MemRegion *superReg = ER->getSuperRegion();
- if (isa<VarRegion>(superReg) || isa<FieldRegion>(superReg) ||
- isa<ObjCIvarRegion>(superReg))
- R = cast<TypedRegion>(superReg);
- }
- // FIXME: What about layers of ElementRegions?
- }
-
- // Mark this region for invalidation. We batch invalidate regions
- // below for efficiency.
- RegionsToInvalidate.push_back(R);
- } else {
- // Nuke all other arguments passed by reference.
- // FIXME: is this necessary or correct? This handles the non-Region
- // cases. Is it ever valid to store to these?
- State = State->unbindLoc(cast<Loc>(V));
- }
- }
-
- // Invalidate designated regions using the batch invalidation API.
-
- // FIXME: We can have collisions on the conjured symbol if the
- // expression *I also creates conjured symbols. We probably want
- // to identify conjured symbols by an expression pair: the enclosing
- // expression (the context) and the expression itself. This should
- // disambiguate conjured symbols.
- unsigned Count = currentBuilderContext->getCurrentBlockCount();
-
- // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate
- // global variables.
- State = State->invalidateRegions(RegionsToInvalidate, CNE, Count, LCtx);
Bldr.generateNode(CNE, Pred, State);
- return;
-
- // FIXME: The below code is long-since dead. However, constructor handling
- // in new-expressions is far from complete. See PR12014 for more details.
-#if 0
- // Evaluate constructor arguments.
- const FunctionProtoType *FnType = NULL;
- const CXXConstructorDecl *CD = CNE->getConstructor();
- if (CD)
- FnType = CD->getType()->getAs<FunctionProtoType>();
- ExplodedNodeSet argsEvaluated;
- Bldr.takeNodes(Pred);
- evalArguments(CNE->constructor_arg_begin(), CNE->constructor_arg_end(),
- FnType, Pred, argsEvaluated);
- Bldr.addNodes(argsEvaluated);
-
- // Initialize the object region and bind the 'new' expression.
- for (ExplodedNodeSet::iterator I = argsEvaluated.begin(),
- E = argsEvaluated.end(); I != E; ++I) {
-
- ProgramStateRef state = (*I)->getState();
-
- // Accumulate list of regions that are invalidated.
- // FIXME: Eventually we should unify the logic for constructor
- // processing in one place.
- SmallVector<const MemRegion*, 10> regionsToInvalidate;
- for (CXXNewExpr::const_arg_iterator
- ai = CNE->constructor_arg_begin(), ae = CNE->constructor_arg_end();
- ai != ae; ++ai)
- {
- SVal val = state->getSVal(*ai, (*I)->getLocationContext());
- if (const MemRegion *region = val.getAsRegion())
- regionsToInvalidate.push_back(region);
- }
-
- if (ObjTy->isRecordType()) {
- regionsToInvalidate.push_back(EleReg);
- // Invalidate the regions.
- // TODO: Pass the call to new information as the last argument, to limit
- // the globals which will get invalidated.
- state = state->invalidateRegions(regionsToInvalidate,
- CNE, blockCount, 0, 0);
-
- } else {
- // Invalidate the regions.
- // TODO: Pass the call to new information as the last argument, to limit
- // the globals which will get invalidated.
- state = state->invalidateRegions(regionsToInvalidate,
- CNE, blockCount, 0, 0);
-
- if (CNE->hasInitializer()) {
- SVal V = state->getSVal(*CNE->constructor_arg_begin(),
- (*I)->getLocationContext());
- state = state->bindLoc(loc::MemRegionVal(EleReg), V);
- } else {
- // Explicitly set to undefined, because currently we retrieve symbolic
- // value from symbolic region.
- state = state->bindLoc(loc::MemRegionVal(EleReg), UndefinedVal());
- }
- }
- state = state->BindExpr(CNE, (*I)->getLocationContext(),
- loc::MemRegionVal(EleReg));
- Bldr.generateNode(CNE, *I, state);
- }
-#endif
}
void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE,
Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp?rev=159608&r1=159607&r2=159608&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp Mon Jul 2 17:21:47 2012
@@ -240,11 +240,6 @@
return false;
}
- // Do not inline constructors until we can model destructors.
- // This is unfortunate, but basically necessary for smart pointers and such.
- if (isa<CXXConstructorDecl>(D))
- return false;
-
// It is possible that the live variables analysis cannot be
// run. If so, bail out.
if (!CalleeADC->getAnalysis<RelaxedLiveVariables>())
@@ -267,10 +262,17 @@
switch (Call.getKind()) {
case CE_Function:
- case CE_CXXConstructor:
case CE_CXXMember:
// These are always at least possible to inline.
break;
+ case CE_CXXConstructor:
+ // Do not inline constructors until we can model destructors.
+ // This is unfortunate, but basically necessary for smart pointers and such.
+ return false;
+ case CE_CXXAllocator:
+ // Do not inline allocators until we model deallocators.
+ // This is unfortunate, but basically necessary for smart pointers and such.
+ return false;
case CE_Block: {
const BlockDataRegion *BR = cast<BlockCall>(Call).getBlockRegion();
if (!BR)
Modified: cfe/trunk/test/Analysis/new.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/new.cpp?rev=159608&r1=159607&r2=159608&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/new.cpp (original)
+++ cfe/trunk/test/Analysis/new.cpp Mon Jul 2 17:21:47 2012
@@ -49,6 +49,16 @@
return y; // no-warning
}
+void *operator new(size_t, void *, void *);
+void *testCustomNewMalloc() {
+ int *x = (int *)malloc(sizeof(int));
+
+ // Should be no-warning (the custom allocator could have freed x).
+ void *y = new (0, x) int; // no-warning
+
+ return y;
+}
+
//--------------------------------
// Incorrectly-modelled behavior
@@ -69,14 +79,3 @@
clang_analyzer_eval(*n == 3); // expected-warning{{UNKNOWN}}
}
-
-void *operator new(size_t, void *, void *);
-void *testCustomNewMalloc() {
- int *x = (int *)malloc(sizeof(int));
-
- // Should be no-warning (the custom allocator could have freed x).
- void *y = new (0, x) int; // expected-warning{{leak of memory pointed to by 'x'}}
-
- return y;
-}
-
More information about the cfe-commits
mailing list