[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.

llvm-svn: 163348
This commit is contained in:
Jordan Rose 2012-09-06 20:37:08 +00:00
parent dc1e35d418
commit 2bc9674b0a
7 changed files with 73 additions and 17 deletions

View File

@ -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>

View File

@ -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,

View File

@ -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;

View File

@ -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);
}

View File

@ -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,

View File

@ -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(),

View File

@ -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;
}
}