[cfe-commits] r67094 - /cfe/trunk/lib/Analysis/CFRefCount.cpp
Ted Kremenek
kremenek at apple.com
Tue Mar 17 12:42:23 PDT 2009
Author: kremenek
Date: Tue Mar 17 14:42:23 2009
New Revision: 67094
URL: http://llvm.org/viewvc/llvm-project?rev=67094&view=rev
Log:
retain/release checker: Add support for reasoning about -dealloc.
Modified:
cfe/trunk/lib/Analysis/CFRefCount.cpp
Modified: cfe/trunk/lib/Analysis/CFRefCount.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFRefCount.cpp?rev=67094&r1=67093&r2=67094&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CFRefCount.cpp (original)
+++ cfe/trunk/lib/Analysis/CFRefCount.cpp Tue Mar 17 14:42:23 2009
@@ -220,12 +220,9 @@
namespace {
/// ArgEffect is used to summarize a function/method call's effect on a
/// particular argument.
-enum ArgEffect { IncRefMsg, IncRef,
- DecRefMsg, DecRef,
- MakeCollectable,
- DoNothing, DoNothingByRef,
- StopTracking, MayEscape, SelfOwn, Autorelease,
- NewAutoreleasePool };
+enum ArgEffect { Autorelease, Dealloc, DecRef, DecRefMsg, DoNothing,
+ DoNothingByRef, IncRefMsg, IncRef, MakeCollectable, MayEscape,
+ NewAutoreleasePool, SelfOwn, StopTracking };
/// ArgEffects summarizes the effects of a function/method call on all of
/// its arguments.
@@ -1155,6 +1152,10 @@
// Create the "drain" selector.
Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef);
addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ);
+
+ // Create the -dealloc summary.
+ Summ = getPersistentSummary(RetEffect::MakeNoRet(), Dealloc);
+ addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ);
// Create the "autorelease" selector.
Summ = getPersistentSummary(E, Autorelease);
@@ -1215,8 +1216,12 @@
Released, // Object has been released.
ReturnedOwned, // Returned object passes ownership to caller.
ReturnedNotOwned, // Return object does not pass ownership to caller.
+ ERROR_START,
+ ErrorDeallocNotOwned, // -dealloc called on non-owned object.
+ ErrorDeallocGC, // Calling -dealloc with GC enabled.
ErrorUseAfterRelease, // Object used after released.
ErrorReleaseNotOwned, // Release of an object that was not owned.
+ ERROR_LEAK_START,
ErrorLeak, // A memory leak due to excessive reference counts.
ErrorLeakReturned // A memory leak due to the returning method not having
// the correct naming conventions.
@@ -1239,14 +1244,16 @@
RetEffect::ObjKind getObjKind() const { return okind; }
- unsigned getCount() const { return Cnt; }
+ unsigned getCount() const { return Cnt; }
+ void clearCounts() { Cnt = 0; }
+
QualType getType() const { return T; }
// Useful predicates.
- static bool isError(Kind k) { return k >= ErrorUseAfterRelease; }
+ static bool isError(Kind k) { return k >= ERROR_START; }
- static bool isLeak(Kind k) { return k >= ErrorLeak; }
+ static bool isLeak(Kind k) { return k >= ERROR_LEAK_START; }
bool isOwned() const {
return getKind() == Owned;
@@ -1269,8 +1276,6 @@
return isError(k) && !isLeak(k);
}
- // State creation: normal state.
-
static RefVal makeOwned(RetEffect::ObjKind o, QualType t,
unsigned Count = 1) {
return RefVal(Owned, o, Count, t);
@@ -1306,7 +1311,7 @@
RefVal operator^(Kind k) const {
return RefVal(k, getObjKind(), getCount(), getType());
}
-
+
void Profile(llvm::FoldingSetNodeID& ID) const {
ID.AddInteger((unsigned) kind);
ID.AddInteger(Cnt);
@@ -1353,6 +1358,14 @@
case Released:
Out << "Released";
break;
+
+ case ErrorDeallocGC:
+ Out << "-dealloc (GC)";
+ break;
+
+ case ErrorDeallocNotOwned:
+ Out << "-dealloc (not-owned)";
+ break;
case ErrorLeak:
Out << "Leaked";
@@ -1439,6 +1452,7 @@
ARCounts::Factory ARCountFactory;
BugType *useAfterRelease, *releaseNotOwned;
+ BugType *deallocGC, *deallocNotOwned;
BugType *leakWithinFunction, *leakAtReturn;
BugReporter *BR;
@@ -1459,7 +1473,8 @@
public:
CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts)
: Summaries(Ctx, gcenabled),
- LOpts(lopts), useAfterRelease(0), releaseNotOwned(0),
+ LOpts(lopts), useAfterRelease(0), releaseNotOwned(0),
+ deallocGC(0), deallocNotOwned(0),
leakWithinFunction(0), leakAtReturn(0), BR(0) {}
virtual ~CFRefCount() {}
@@ -2150,9 +2165,39 @@
NewAutoreleasePool; break;
}
+ // Handle all use-after-releases.
+ if (!isGCEnabled() && V.getKind() == RefVal::Released) {
+ V = V ^ RefVal::ErrorUseAfterRelease;
+ hasErr = V.getKind();
+ return state.set<RefBindings>(sym, V);
+ }
+
switch (E) {
default:
assert (false && "Unhandled CFRef transition.");
+
+ case Dealloc:
+ // Any use of -dealloc in GC is *bad*.
+ if (isGCEnabled()) {
+ V = V ^ RefVal::ErrorDeallocGC;
+ hasErr = V.getKind();
+ break;
+ }
+
+ switch (V.getKind()) {
+ default:
+ assert(false && "Invalid case.");
+ case RefVal::Owned:
+ // The object immediately transitions to the released state.
+ V = V ^ RefVal::Released;
+ V.clearCounts();
+ return state.set<RefBindings>(sym, V);
+ case RefVal::NotOwned:
+ V = V ^ RefVal::ErrorDeallocNotOwned;
+ hasErr = V.getKind();
+ break;
+ }
+ break;
case NewAutoreleasePool:
assert(!isGCEnabled());
@@ -2163,20 +2208,19 @@
V = V ^ RefVal::NotOwned;
break;
}
+
// Fall-through.
case DoNothingByRef:
case DoNothing:
- if (!isGCEnabled() && V.getKind() == RefVal::Released) {
- V = V ^ RefVal::ErrorUseAfterRelease;
- hasErr = V.getKind();
- break;
- }
return state;
case Autorelease:
- if (isGCEnabled()) return state;
- // Fall-through.
+ if (isGCEnabled())
+ return state;
+
+ // Fall-through.
+
case StopTracking:
return state.remove<RefBindings>(sym);
@@ -2190,12 +2234,9 @@
V = V + 1;
break;
case RefVal::Released:
- if (isGCEnabled())
- V = (V ^ RefVal::Owned) + 1;
- else {
- V = V ^ RefVal::ErrorUseAfterRelease;
- hasErr = V.getKind();
- }
+ // Non-GC cases are handled above.
+ assert(isGCEnabled());
+ V = (V ^ RefVal::Owned) + 1;
break;
}
break;
@@ -2206,6 +2247,7 @@
case DecRef:
switch (V.getKind()) {
default:
+ // case 'RefVal::Released' handled above.
assert (false);
case RefVal::Owned:
@@ -2222,11 +2264,13 @@
hasErr = V.getKind();
}
break;
-
+
case RefVal::Released:
+ // Non-GC cases are handled above.
+ assert(isGCEnabled());
V = V ^ RefVal::ErrorUseAfterRelease;
hasErr = V.getKind();
- break;
+ break;
}
break;
}
@@ -2281,6 +2325,26 @@
}
};
+ class VISIBILITY_HIDDEN DeallocGC : public CFRefBug {
+ public:
+ DeallocGC(CFRefCount *tf) : CFRefBug(tf,
+ "-dealloc called while using GC") {}
+
+ const char *getDescription() const {
+ return "-dealloc called while using GC";
+ }
+ };
+
+ class VISIBILITY_HIDDEN DeallocNotOwned : public CFRefBug {
+ public:
+ DeallocNotOwned(CFRefCount *tf) : CFRefBug(tf,
+ "-dealloc sent to non-exclusively owned object") {}
+
+ const char *getDescription() const {
+ return "-dealloc sent to object that may be referenced elsewhere";
+ }
+ };
+
class VISIBILITY_HIDDEN Leak : public CFRefBug {
const bool isReturn;
protected:
@@ -2372,6 +2436,12 @@
releaseNotOwned = new BadRelease(this);
BR.Register(releaseNotOwned);
+ deallocGC = new DeallocGC(this);
+ BR.Register(deallocGC);
+
+ deallocNotOwned = new DeallocNotOwned(this);
+ BR.Register(deallocNotOwned);
+
// First register "return" leaks.
const char* name = 0;
@@ -2559,6 +2629,19 @@
do {
// Get the previous type state.
RefVal PrevV = *PrevT;
+
+ // Specially handle -dealloc.
+ if (!TF.isGCEnabled() && contains(AEffects, Dealloc)) {
+ // Determine if the object's reference count was pushed to zero.
+ assert(!(PrevV == CurrV) && "The typestate *must* have changed.");
+ // We may not have transitioned to 'release' if we hit an error.
+ // This case is handled elsewhere.
+ if (CurrV.getKind() == RefVal::Released) {
+ assert(CurrV.getCount() == 0);
+ os << "Object released by directly sending the '-dealloc' message";
+ break;
+ }
+ }
// Specially handle CFMakeCollectable and friends.
if (contains(AEffects, MakeCollectable)) {
@@ -2737,10 +2820,9 @@
PathDiagnosticPiece*
CFRefReport::getEndPath(BugReporter& br, const ExplodedNode<GRState>* EndN) {
-
- GRBugReporter& BR = cast<GRBugReporter>(br);
// Tell the BugReporter to report cases when the tracked symbol is
// assigned to different variables, etc.
+ GRBugReporter& BR = cast<GRBugReporter>(br);
cast<GRBugReporter>(BR).addNotableSymbol(Sym);
return RangedBugReport::getEndPath(BR, EndN);
}
@@ -3016,11 +3098,22 @@
CFRefBug *BT = 0;
- if (hasErr == RefVal::ErrorUseAfterRelease)
- BT = static_cast<CFRefBug*>(useAfterRelease);
- else {
- assert(hasErr == RefVal::ErrorReleaseNotOwned);
- BT = static_cast<CFRefBug*>(releaseNotOwned);
+ switch (hasErr) {
+ default:
+ assert(false && "Unhandled error.");
+ return;
+ case RefVal::ErrorUseAfterRelease:
+ BT = static_cast<CFRefBug*>(useAfterRelease);
+ break;
+ case RefVal::ErrorReleaseNotOwned:
+ BT = static_cast<CFRefBug*>(releaseNotOwned);
+ break;
+ case RefVal::ErrorDeallocGC:
+ BT = static_cast<CFRefBug*>(deallocGC);
+ break;
+ case RefVal::ErrorDeallocNotOwned:
+ BT = static_cast<CFRefBug*>(deallocNotOwned);
+ break;
}
CFRefReport *report = new CFRefReport(*BT, *this, N, Sym);
More information about the cfe-commits
mailing list