aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Rose <jordan_rose@apple.com>2012-09-06 20:37:08 +0000
committerJordan Rose <jordan_rose@apple.com>2012-09-06 20:37:08 +0000
commit200fa2e70d52ae6d620e81cd45536071fdde70c0 (patch)
treec537add77f949862f423e48349aa4d84fd2963b3
parentc69164917460ff717fd0b02649a3d7b0c5096088 (diff)
[analyzer] Don't attempt to devirtualize calls to base class destructors.
CXXDestructorCall now has a flag for when it is a base destructor call. Other kinds of destructor calls (locals, fields, temporaries, and 'delete') all behave as "whole-object" destructors and do not behave differently from one another (specifically, in these cases we /should/ try to devirtualize a call to a virtual destructor). This was causing crashes in both our internal buildbot, the crash still being tracked in PR13765, and some of the crashes being tracked in PR13763, due to a assertion failure. (The behavior under -Asserts happened to be correct anyway.) Adding this knowledge also allows our DynamicTypePropagation checker to do a bit less work; the special rules about virtual method calls during a destructor only require extra handling during base destructors. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@163348 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h28
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h4
-rw-r--r--lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp6
-rw-r--r--lib/StaticAnalyzer/Core/CallEvent.cpp13
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp6
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCXX.cpp3
-rw-r--r--test/Analysis/dtor.cpp30
7 files changed, 73 insertions, 17 deletions
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
index d9712208f0..912eea0b37 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -604,6 +604,8 @@ class CXXDestructorCall : public CXXInstanceCall {
friend class CallEventManager;
protected:
+ typedef llvm::PointerIntPair<const MemRegion *, 1, bool> DtorDataTy;
+
/// Creates an implicit destructor.
///
/// \param DD The destructor that will be called.
@@ -612,10 +614,10 @@ protected:
/// \param St The path-sensitive state at this point in the program.
/// \param LCtx The location context at this point in the program.
CXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger,
- const MemRegion *Target, ProgramStateRef St,
- const LocationContext *LCtx)
+ const MemRegion *Target, bool IsBaseDestructor,
+ ProgramStateRef St, const LocationContext *LCtx)
: CXXInstanceCall(DD, St, LCtx) {
- Data = Target;
+ Data = DtorDataTy(Target, IsBaseDestructor).getOpaqueValue();
Location = Trigger->getLocEnd();
}
@@ -626,9 +628,16 @@ public:
virtual SourceRange getSourceRange() const { return Location; }
virtual unsigned getNumArgs() const { return 0; }
+ virtual RuntimeDefinition getRuntimeDefinition() const;
+
/// \brief Returns the value of the implicit 'this' object.
virtual SVal getCXXThisVal() const;
+ /// Returns true if this is a call to a base class destructor.
+ bool isBaseDestructor() const {
+ return DtorDataTy::getFromOpaqueValue(Data).getInt();
+ }
+
virtual Kind getKind() const { return CE_CXXDestructor; }
static bool classof(const CallEvent *CA) {
@@ -882,6 +891,13 @@ class CallEventManager {
return new (allocate()) T(A1, A2, A3, St, LCtx);
}
+ template <typename T, typename Arg1, typename Arg2, typename Arg3,
+ typename Arg4>
+ T *create(Arg1 A1, Arg2 A2, Arg3 A3, Arg4 A4, ProgramStateRef St,
+ const LocationContext *LCtx) {
+ return new (allocate()) T(A1, A2, A3, A4, St, LCtx);
+ }
+
public:
CallEventManager(llvm::BumpPtrAllocator &alloc) : Alloc(alloc) {}
@@ -908,9 +924,9 @@ public:
CallEventRef<CXXDestructorCall>
getCXXDestructorCall(const CXXDestructorDecl *DD, const Stmt *Trigger,
- const MemRegion *Target, ProgramStateRef State,
- const LocationContext *LCtx) {
- return create<CXXDestructorCall>(DD, Trigger, Target, State, LCtx);
+ const MemRegion *Target, bool IsBase,
+ ProgramStateRef State, const LocationContext *LCtx) {
+ return create<CXXDestructorCall>(DD, Trigger, Target, IsBase, State, LCtx);
}
CallEventRef<CXXAllocatorCall>
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index ab15581938..ffc1698891 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -385,8 +385,8 @@ public:
void VisitCXXConstructExpr(const CXXConstructExpr *E, ExplodedNode *Pred,
ExplodedNodeSet &Dst);
- void VisitCXXDestructor(QualType ObjectType,
- const MemRegion *Dest, const Stmt *S,
+ void VisitCXXDestructor(QualType ObjectType, const MemRegion *Dest,
+ const Stmt *S, bool IsBaseDtor,
ExplodedNode *Pred, ExplodedNodeSet &Dst);
void VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
index b636efbe35..95cc6f2287 100644
--- a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
+++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -83,14 +83,14 @@ void DynamicTypePropagation::checkPreCall(const CallEvent &Call,
if (const CXXDestructorCall *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
// C++11 [class.cdtor]p4 (see above)
+ if (!Dtor->isBaseDestructor())
+ return;
const MemRegion *Target = Dtor->getCXXThisVal().getAsRegion();
if (!Target)
return;
- // FIXME: getRuntimeDefinition() can be expensive. It would be better to do
- // this when we are entering the stack frame for the destructor.
- const Decl *D = Dtor->getRuntimeDefinition().getDecl();
+ const Decl *D = Dtor->getDecl();
if (!D)
return;
diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp
index 1cfa394a07..7d58e806a7 100644
--- a/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -559,10 +559,19 @@ void CXXConstructorCall::getInitialStackFrameContents(
SVal CXXDestructorCall::getCXXThisVal() const {
if (Data)
- return loc::MemRegionVal(static_cast<const MemRegion *>(Data));
+ return loc::MemRegionVal(DtorDataTy::getFromOpaqueValue(Data).getPointer());
return UnknownVal();
}
+RuntimeDefinition CXXDestructorCall::getRuntimeDefinition() const {
+ // Base destructors are always called non-virtually.
+ // Skip CXXInstanceCall's devirtualization logic in this case.
+ if (isBaseDestructor())
+ return AnyFunctionCall::getRuntimeDefinition();
+
+ return CXXInstanceCall::getRuntimeDefinition();
+}
+
CallEvent::param_iterator ObjCMethodCall::param_begin() const {
const ObjCMethodDecl *D = getDecl();
@@ -892,5 +901,5 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
Trigger = Dtor->getBody();
return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(),
- State, CallerCtx);
+ isa<CFGBaseDtor>(E), State, CallerCtx);
}
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index b95a3fedb7..d177b60a50 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -446,7 +446,7 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
Loc dest = state->getLValue(varDecl, Pred->getLocationContext());
VisitCXXDestructor(varType, cast<loc::MemRegionVal>(dest).getRegion(),
- Dtor.getTriggerStmt(), Pred, Dst);
+ Dtor.getTriggerStmt(), /*IsBase=*/false, Pred, Dst);
}
void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D,
@@ -464,7 +464,7 @@ void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D,
SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy);
VisitCXXDestructor(BaseTy, cast<loc::MemRegionVal>(BaseVal).getRegion(),
- CurDtor->getBody(), Pred, Dst);
+ CurDtor->getBody(), /*IsBase=*/true, Pred, Dst);
}
void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
@@ -480,7 +480,7 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
VisitCXXDestructor(Member->getType(),
cast<loc::MemRegionVal>(FieldVal).getRegion(),
- CurDtor->getBody(), Pred, Dst);
+ CurDtor->getBody(), /*IsBase=*/false, Pred, Dst);
}
void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index d3d2837fe5..10ecb3b9a9 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -163,6 +163,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
void ExprEngine::VisitCXXDestructor(QualType ObjectType,
const MemRegion *Dest,
const Stmt *S,
+ bool IsBaseDtor,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
const LocationContext *LCtx = Pred->getLocationContext();
@@ -183,7 +184,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
CallEventManager &CEMgr = getStateManager().getCallEventManager();
CallEventRef<CXXDestructorCall> Call =
- CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, State, LCtx);
+ CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx);
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
Call->getSourceRange().getBegin(),
diff --git a/test/Analysis/dtor.cpp b/test/Analysis/dtor.cpp
index a762ebed12..99c47d5920 100644
--- a/test/Analysis/dtor.cpp
+++ b/test/Analysis/dtor.cpp
@@ -251,3 +251,33 @@ namespace DestructorsShouldNotAffectReturnValues {
free(p); // no-warning
}
}
+
+namespace MultipleInheritanceVirtualDtors {
+ class VirtualDtor {
+ protected:
+ virtual ~VirtualDtor() {
+ clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
+ }
+ };
+
+ class NonVirtualDtor {
+ protected:
+ ~NonVirtualDtor() {
+ clang_analyzer_checkInlined(true); // expected-warning{{TRUE}}
+ }
+ };
+
+ class SubclassA : public VirtualDtor, public NonVirtualDtor {
+ public:
+ virtual ~SubclassA() {}
+ };
+ class SubclassB : public NonVirtualDtor, public VirtualDtor {
+ public:
+ virtual ~SubclassB() {}
+ };
+
+ void test() {
+ SubclassA a;
+ SubclassB b;
+ }
+}