diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp | 133 |
1 files changed, 111 insertions, 22 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 0e1104730cd..9ac993160a2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -94,29 +94,70 @@ public: }; private: - Kind kind; - RetEffect::ObjKind okind; + /// The number of outstanding retains. unsigned Cnt; + /// The number of outstanding autoreleases. unsigned ACnt; + /// The (static) type of the object at the time we started tracking it. QualType T; - RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t) - : kind(k), okind(o), Cnt(cnt), ACnt(acnt), T(t) {} + /// The current state of the object. + /// + /// See the RefVal::Kind enum for possible values. + unsigned RawKind : 5; + + /// The kind of object being tracked (CF or ObjC), if known. + /// + /// See the RetEffect::ObjKind enum for possible values. + unsigned RawObjectKind : 2; + + /// True if the current state and/or retain count may turn out to not be the + /// best possible approximation of the reference counting state. + /// + /// If true, the checker may decide to throw away ("override") this state + /// in favor of something else when it sees the object being used in new ways. + /// + /// This setting should not be propagated to state derived from this state. + /// Once we start deriving new states, it would be inconsistent to override + /// them. + unsigned IsOverridable : 1; + + RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t, + bool Overridable = false) + : Cnt(cnt), ACnt(acnt), T(t), RawKind(static_cast<unsigned>(k)), + RawObjectKind(static_cast<unsigned>(o)), IsOverridable(Overridable) { + assert(getKind() == k && "not enough bits for the kind"); + assert(getObjKind() == o && "not enough bits for the object kind"); + } public: - Kind getKind() const { return kind; } + Kind getKind() const { return static_cast<Kind>(RawKind); } - RetEffect::ObjKind getObjKind() const { return okind; } + RetEffect::ObjKind getObjKind() const { + return static_cast<RetEffect::ObjKind>(RawObjectKind); + } unsigned getCount() const { return Cnt; } unsigned getAutoreleaseCount() const { return ACnt; } unsigned getCombinedCounts() const { return Cnt + ACnt; } - void clearCounts() { Cnt = 0; ACnt = 0; } - void setCount(unsigned i) { Cnt = i; } - void setAutoreleaseCount(unsigned i) { ACnt = i; } + void clearCounts() { + Cnt = 0; + ACnt = 0; + IsOverridable = false; + } + void setCount(unsigned i) { + Cnt = i; + IsOverridable = false; + } + void setAutoreleaseCount(unsigned i) { + ACnt = i; + IsOverridable = false; + } QualType getType() const { return T; } + bool isOverridable() const { return IsOverridable; } + bool isOwned() const { return getKind() == Owned; } @@ -133,20 +174,31 @@ public: return getKind() == ReturnedNotOwned; } + /// Create a state for an object whose lifetime is the responsibility of the + /// current function, at least partially. + /// + /// Most commonly, this is an owned object with a retain count of +1. static RefVal makeOwned(RetEffect::ObjKind o, QualType t, unsigned Count = 1) { return RefVal(Owned, o, Count, 0, t); } + /// Create a state for an object whose lifetime is not the responsibility of + /// the current function. + /// + /// Most commonly, this is an unowned object with a retain count of +0. static RefVal makeNotOwned(RetEffect::ObjKind o, QualType t, unsigned Count = 0) { return RefVal(NotOwned, o, Count, 0, t); } - // Comparison, profiling, and pretty-printing. - - bool operator==(const RefVal& X) const { - return kind == X.kind && Cnt == X.Cnt && T == X.T && ACnt == X.ACnt; + /// Create an "overridable" state for an unowned object at +0. + /// + /// An overridable state is one that provides a good approximation of the + /// reference counting state now, but which may be discarded later if the + /// checker sees the object being used in new ways. + static RefVal makeOverridableNotOwned(RetEffect::ObjKind o, QualType t) { + return RefVal(NotOwned, o, 0, 0, t, /*Overridable=*/true); } RefVal operator-(size_t i) const { @@ -169,11 +221,24 @@ public: getType()); } + // Comparison, profiling, and pretty-printing. + + bool hasSameState(const RefVal &X) const { + return getKind() == X.getKind() && Cnt == X.Cnt && ACnt == X.ACnt; + } + + bool operator==(const RefVal& X) const { + return T == X.T && hasSameState(X) && getObjKind() == X.getObjKind() && + IsOverridable == X.IsOverridable; + } + void Profile(llvm::FoldingSetNodeID& ID) const { - ID.AddInteger((unsigned) kind); + ID.Add(T); + ID.AddInteger(RawKind); ID.AddInteger(Cnt); ID.AddInteger(ACnt); - ID.Add(T); + ID.AddInteger(RawObjectKind); + ID.AddBoolean(IsOverridable); } void print(raw_ostream &Out) const; @@ -183,6 +248,9 @@ void RefVal::print(raw_ostream &Out) const { if (!T.isNull()) Out << "Tracked " << T.getAsString() << '/'; + if (isOverridable()) + Out << "(overridable) "; + switch (getKind()) { default: llvm_unreachable("Invalid RefVal kind"); case Owned: { @@ -1923,7 +1991,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, if (!GCEnabled && std::find(AEffects.begin(), AEffects.end(), Dealloc) != AEffects.end()) { // Determine if the object's reference count was pushed to zero. - assert(!(PrevV == CurrV) && "The typestate *must* have changed."); + assert(!PrevV.hasSameState(CurrV) && "The state should have changed."); // We may not have transitioned to 'release' if we hit an error. // This case is handled elsewhere. if (CurrV.getKind() == RefVal::Released) { @@ -1944,7 +2012,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, if (GCEnabled) { // Determine if the object's reference count was pushed to zero. - assert(!(PrevV == CurrV) && "The typestate *must* have changed."); + assert(!PrevV.hasSameState(CurrV) && "The state should have changed."); os << "In GC mode a call to '" << *FD << "' decrements an object's retain count and registers the " @@ -1969,7 +2037,7 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N, } // Determine if the typestate has changed. - if (!(PrevV == CurrV)) + if (!PrevV.hasSameState(CurrV)) switch (CurrV.getKind()) { case RefVal::Owned: case RefVal::NotOwned: @@ -2333,6 +2401,7 @@ class RetainCountChecker check::PostStmt<ObjCArrayLiteral>, check::PostStmt<ObjCDictionaryLiteral>, check::PostStmt<ObjCBoxedExpr>, + check::PostStmt<ObjCIvarRefExpr>, check::PostCall, check::PreStmt<ReturnStmt>, check::RegionChanges, @@ -2482,6 +2551,8 @@ public: void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const; void checkPostStmt(const ObjCBoxedExpr *BE, CheckerContext &C) const; + void checkPostStmt(const ObjCIvarRefExpr *IRE, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; void checkSummary(const RetainSummary &Summ, const CallEvent &Call, @@ -2699,6 +2770,20 @@ void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex, C.addTransition(State); } +void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + // If an instance variable was previously accessed through a property, + // it may have a synthesized refcount of +0. Override right now that we're + // doing direct access. + if (Optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>()) + if (SymbolRef Sym = State->getSVal(*IVarLoc).getAsSymbol()) + if (const RefVal *RV = getRefBinding(State, Sym)) + if (RV->isOverridable()) + State = removeRefBinding(State, Sym); + C.addTransition(State); +} + void RetainCountChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { RetainSummaryManager &Summaries = getSummaryManager(C); @@ -2785,12 +2870,16 @@ void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ, state = removeRefBinding(state, Sym); } else if (RE.getKind() == RetEffect::NotOwnedSymbol) { if (wasSynthesizedProperty(MsgInvocation, C.getPredecessor())) { - // Believe the summary if we synthesized the body and the return value is - // untracked. This handles property getters. + // Believe the summary if we synthesized the body of a property getter + // and the return value is currently untracked. If the corresponding + // instance variable is later accessed directly, however, we're going to + // want to override this state, so that the owning object can perform + // reference counting operations on its own ivars. SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol(); if (Sym && !getRefBinding(state, Sym)) - state = setRefBinding(state, Sym, RefVal::makeNotOwned(RE.getObjKind(), - Sym->getType())); + state = setRefBinding(state, Sym, + RefVal::makeOverridableNotOwned(RE.getObjKind(), + Sym->getType())); } } |