From: Jordy Rose Date: Sun, 21 Aug 2011 19:41:36 +0000 (+0000) Subject: [analyzer] Migrate return value handling from CFRefCount to ExprEngine. This seems... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=500abad7edfcc2409b18dd616cdbc28a094926f5;p=clang [analyzer] Migrate return value handling from CFRefCount to ExprEngine. This seems to result in a minor performance hit, but I think that will go away again once we eliminate TransferFuncs from function calls entirely. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@138220 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index b1dfcc05b0..1bdd57291f 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -408,10 +408,7 @@ public: protected: void evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg, - ExplodedNode *Pred, const ProgramState *state) { - assert (Builder && "StmtNodeBuilder must be defined."); - getTF().evalObjCMessage(Dst, *this, *Builder, msg, Pred, state); - } + ExplodedNode *Pred, const ProgramState *state); const ProgramState *MarkBranch(const ProgramState *St, const Stmt *Terminator, bool branchTaken); diff --git a/lib/StaticAnalyzer/Core/CFRefCount.cpp b/lib/StaticAnalyzer/Core/CFRefCount.cpp index 7cffdb2388..33ea9f28b6 100644 --- a/lib/StaticAnalyzer/Core/CFRefCount.cpp +++ b/lib/StaticAnalyzer/Core/CFRefCount.cpp @@ -140,7 +140,6 @@ class RetEffect { public: enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol, NotOwnedSymbol, GCNotOwnedSymbol, ARCNotOwnedSymbol, - ReceiverAlias, OwnedWhenTrackedReceiver }; enum ObjKind { CF, ObjC, AnyObj }; @@ -175,9 +174,6 @@ public: static RetEffect MakeAlias(unsigned Idx) { return RetEffect(Alias, Idx); } - static RetEffect MakeReceiverAlias() { - return RetEffect(ReceiverAlias); - } static RetEffect MakeOwned(ObjKind o, bool isAllocated = false) { return RetEffect(isAllocated ? OwnedAllocatedSymbol : OwnedSymbol, o); } @@ -1485,30 +1481,29 @@ void RetainSummaryManager::InitializeMethodSummaries() { getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); // Create the "retain" selector. - RetEffect E = RetEffect::MakeReceiverAlias(); - RetainSummary *Summ = getPersistentSummary(E, IncRefMsg); + RetEffect NoRet = RetEffect::MakeNoRet(); + RetainSummary *Summ = getPersistentSummary(NoRet, IncRefMsg); addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ); // Create the "release" selector. - Summ = getPersistentSummary(E, DecRefMsg); + Summ = getPersistentSummary(NoRet, DecRefMsg); addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ); // Create the "drain" selector. - Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef); + Summ = getPersistentSummary(NoRet, isGCEnabled() ? DoNothing : DecRef); addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ); // Create the -dealloc summary. - Summ = getPersistentSummary(RetEffect::MakeNoRet(), Dealloc); + Summ = getPersistentSummary(NoRet, Dealloc); addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ); // Create the "autorelease" selector. - Summ = getPersistentSummary(E, Autorelease); + Summ = getPersistentSummary(NoRet, Autorelease); addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ); // Specially handle NSAutoreleasePool. addInstMethSummary("NSAutoreleasePool", "init", - getPersistentSummary(RetEffect::MakeReceiverAlias(), - NewAutoreleasePool)); + getPersistentSummary(NoRet, NewAutoreleasePool)); // For NSWindow, allocated objects are (initially) self-owned. // FIXME: For now we opt for false negatives with NSWindow, as these objects @@ -2738,31 +2733,15 @@ void CFRefCount::evalSummary(ExplodedNodeSet &Dst, switch (RE.getKind()) { default: - assert (false && "Unhandled RetEffect."); break; - - case RetEffect::NoRet: { - // Make up a symbol for the return value (not reference counted). - // FIXME: Most of this logic is not specific to the retain/release - // checker. - - // FIXME: We eventually should handle structs and other compound types - // that are returned by value. - - // Use the result type from callOrMsg as it automatically adjusts - // for methods/functions that return references. - QualType resultTy = callOrMsg.getResultType(Eng.getContext()); - if (Loc::isLocType(resultTy) || - (resultTy->isIntegerType() && resultTy->isScalarType())) { - unsigned Count = Builder.getCurrentBlockCount(); - SValBuilder &svalBuilder = Eng.getSValBuilder(); - SVal X = svalBuilder.getConjuredSymbolVal(NULL, Ex, resultTy, Count); - state = state->BindExpr(Ex, X, false); - } + llvm_unreachable("Unhandled RetEffect."); break; + case RetEffect::NoRet: + // No work necessary. break; - } case RetEffect::Alias: { + // FIXME: This should be moved to an eval::call check and limited to the + // specific functions that return aliases of their arguments. unsigned idx = RE.getIndex(); assert (idx < callOrMsg.getNumArgs()); SVal V = callOrMsg.getArgSValAsScalarOrLoc(idx); @@ -2770,28 +2749,18 @@ void CFRefCount::evalSummary(ExplodedNodeSet &Dst, break; } - case RetEffect::ReceiverAlias: { - assert(Receiver); - SVal V = Receiver.getSValAsScalarOrLoc(state); - state = state->BindExpr(Ex, V, false); - break; - } - case RetEffect::OwnedAllocatedSymbol: - case RetEffect::OwnedSymbol: { - unsigned Count = Builder.getCurrentBlockCount(); - SValBuilder &svalBuilder = Eng.getSValBuilder(); - SymbolRef Sym = svalBuilder.getConjuredSymbol(Ex, Count); - - // Use the result type from callOrMsg as it automatically adjusts - // for methods/functions that return references. - QualType resultTy = callOrMsg.getResultType(Eng.getContext()); - state = state->set(Sym, RefVal::makeOwned(RE.getObjKind(), - resultTy)); - state = state->BindExpr(Ex, svalBuilder.makeLoc(Sym), false); + case RetEffect::OwnedSymbol: + if (SymbolRef Sym = state->getSVal(Ex).getAsSymbol()) { + // Use the result type from callOrMsg as it automatically adjusts + // for methods/functions that return references. + QualType ResultTy = callOrMsg.getResultType(Eng.getContext()); + state = state->set(Sym, RefVal::makeOwned(RE.getObjKind(), + ResultTy)); + } // FIXME: Add a flag to the checker where allocations are assumed to - // *not fail. + // *not* fail. (The code below is out-of-date, though.) #if 0 if (RE.getKind() == RetEffect::OwnedAllocatedSymbol) { bool isFeasible; @@ -2801,18 +2770,17 @@ void CFRefCount::evalSummary(ExplodedNodeSet &Dst, #endif break; - } case RetEffect::GCNotOwnedSymbol: case RetEffect::ARCNotOwnedSymbol: case RetEffect::NotOwnedSymbol: { - unsigned Count = Builder.getCurrentBlockCount(); - SValBuilder &svalBuilder = Eng.getSValBuilder(); - SymbolRef Sym = svalBuilder.getConjuredSymbol(Ex, Count); - QualType RetT = GetReturnType(Ex, svalBuilder.getContext()); - state = state->set(Sym, RefVal::makeNotOwned(RE.getObjKind(), - RetT)); - state = state->BindExpr(Ex, svalBuilder.makeLoc(Sym), false); + if (SymbolRef Sym = state->getSVal(Ex).getAsSymbol()) { + // Use GetReturnType in order to give [NSFoo alloc] the type NSFoo *. + QualType ResultTy = GetReturnType(Ex, Eng.getContext()); + state = + state->set(Sym, RefVal::makeNotOwned(RE.getObjKind(), + ResultTy)); + } break; } } diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 03e82ddccc..e49d376d66 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -83,18 +83,40 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, Eng.InlineCall(Dst, CE, Pred)) { return; } - + + // First handle the return value. StmtNodeBuilder &Builder = Eng.getBuilder(); assert(&Builder && "StmtNodeBuilder must be defined."); - - // Dispatch to the plug-in transfer function. - unsigned oldSize = Dst.size(); - SaveOr OldHasGen(Builder.hasGeneratedNode); - - // Dispatch to transfer function logic to handle the call itself. + + // Get the callee. const Expr *Callee = CE->getCallee()->IgnoreParens(); const ProgramState *state = Pred->getState(); SVal L = state->getSVal(Callee); + + // Figure out the result type. We do this dance to handle references. + QualType ResultTy; + if (const FunctionDecl *FD = L.getAsFunctionDecl()) + ResultTy = FD->getResultType(); + else + ResultTy = CE->getType(); + + if (CE->isLValue()) + ResultTy = Eng.getContext().getPointerType(ResultTy); + + // Conjure a symbol value to use as the result. + SValBuilder &SVB = Eng.getSValBuilder(); + unsigned Count = Builder.getCurrentBlockCount(); + SVal RetVal = SVB.getConjuredSymbolVal(0, CE, ResultTy, Count); + + // Generate a new ExplodedNode with the return value set. + state = state->BindExpr(CE, RetVal); + Pred = Builder.generateNode(CE, state, Pred); + + // Then handle everything else. + unsigned oldSize = Dst.size(); + SaveOr OldHasGen(Builder.hasGeneratedNode); + + // Dispatch to transfer function logic to handle the rest of the call. Eng.getTF().evalCall(Dst, Eng, Builder, CE, L, Pred); // Handle the case where no nodes where generated. Auto-generate that diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp index 34426defec..66cef61d99 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -237,3 +237,44 @@ void ExprEngine::VisitObjCMessage(const ObjCMessage &msg, // the created nodes in 'Dst'. getCheckerManager().runCheckersForPostObjCMessage(Dst, dstEval, msg, *this); } + +void ExprEngine::evalObjCMessage(ExplodedNodeSet &Dst, const ObjCMessage &msg, + ExplodedNode *Pred, + const ProgramState *state) { + assert (Builder && "StmtNodeBuilder must be defined."); + + // First handle the return value. + SVal ReturnValue = UnknownVal(); + + // Some method families have known return values. + switch (msg.getMethodFamily()) { + default: + break; + case OMF_autorelease: + case OMF_retain: + case OMF_self: { + // These methods return their receivers. + // FIXME: Should OMF_init be included here? + const Expr *ReceiverE = msg.getInstanceReceiver(); + if (ReceiverE) + ReturnValue = state->getSVal(ReceiverE); + break; + } + } + + // If we failed to figure out the return value, use a conjured value instead. + if (ReturnValue.isUnknown()) { + SValBuilder &SVB = getSValBuilder(); + QualType ResultTy = msg.getResultType(getContext()); + unsigned Count = Builder->getCurrentBlockCount(); + const Expr *CurrentE = cast(currentStmt); + ReturnValue = SVB.getConjuredSymbolVal(NULL, CurrentE, ResultTy, Count); + } + + // Bind the return value. + state = state->BindExpr(currentStmt, ReturnValue); + + // Now we can handle the other aspects of the message. + getTF().evalObjCMessage(Dst, *this, *Builder, msg, Pred, state); +} +