forked from OSchip/llvm-project
[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:
parent
dc1e35d418
commit
2bc9674b0a
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue