Prototype (pre-alpha) implementation of CFRef checker.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@48272 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/Analysis/CFRefCount.cpp b/Analysis/CFRefCount.cpp
index ea9c8bf..a1f63b1 100644
--- a/Analysis/CFRefCount.cpp
+++ b/Analysis/CFRefCount.cpp
@@ -86,6 +86,10 @@
     return (*Args)[idx];
   }
   
+  RetEffect getRet() const {
+    return Ret;
+  }
+  
   typedef ArgEffects::const_iterator arg_iterator;
   
   arg_iterator begin_args() const { return Args->begin(); }
@@ -113,12 +117,24 @@
   llvm::BumpPtrAllocator BPAlloc;
   
   ArgEffects             ScratchArgs;
+  
+  
+  ArgEffects*   getArgEffects();
 
+  CFRefSummary* getCannedCFSummary(FunctionTypeProto* FT, bool isRetain);
+
+  CFRefSummary* getCFSummary(FunctionDecl* FD, const char* FName);
+  
+  CFRefSummary* getCFSummaryCreateRule(FunctionTypeProto* FT);
+  CFRefSummary* getCFSummaryGetRule(FunctionTypeProto* FT);  
+  
+  CFRefSummary* getPersistentSummary(ArgEffects* AE, RetEffect RE);
+  
 public:
   CFRefSummaryManager() {}
   ~CFRefSummaryManager();
   
-  CFRefSummary* getSummary(FunctionDecl* FD);
+  CFRefSummary* getSummary(FunctionDecl* FD, ASTContext& Ctx);
 };
   
 } // end anonymous namespace
@@ -137,7 +153,59 @@
     I->getValue().~ArgEffects();
 }
 
-CFRefSummary* CFRefSummaryManager::getSummary(FunctionDecl* FD) {
+ArgEffects* CFRefSummaryManager::getArgEffects() {
+
+  assert (!ScratchArgs.empty());
+  
+  llvm::FoldingSetNodeID profile;
+  profile.Add(ScratchArgs);
+  void* InsertPos;
+  
+  llvm::FoldingSetNodeWrapper<ArgEffects>* E =
+    AESet.FindNodeOrInsertPos(profile, InsertPos);
+  
+  if (E) {    
+    ScratchArgs.clear();
+    return &E->getValue();
+  }
+  
+  E = (llvm::FoldingSetNodeWrapper<ArgEffects>*)
+      BPAlloc.Allocate<llvm::FoldingSetNodeWrapper<ArgEffects> >();
+                       
+  new (E) llvm::FoldingSetNodeWrapper<ArgEffects>(ScratchArgs);
+  AESet.InsertNode(E, InsertPos);
+
+  ScratchArgs.clear();
+  return &E->getValue();
+}
+
+CFRefSummary* CFRefSummaryManager::getPersistentSummary(ArgEffects* AE,
+                                                        RetEffect RE) {
+  
+  llvm::FoldingSetNodeID profile;
+  CFRefSummary::Profile(profile, AE, RE);
+  void* InsertPos;
+  
+  CFRefSummary* Summ = SummarySet.FindNodeOrInsertPos(profile, InsertPos);
+  
+  if (Summ)
+    return Summ;
+  
+  Summ = (CFRefSummary*) BPAlloc.Allocate<CFRefSummary>();
+  new (Summ) CFRefSummary(AE, RE);
+  SummarySet.InsertNode(Summ, InsertPos);
+  
+  return Summ;
+}
+
+
+CFRefSummary* CFRefSummaryManager::getSummary(FunctionDecl* FD,
+                                              ASTContext& Ctx) {
+
+  SourceLocation Loc = FD->getLocation();
+  
+  if (!Loc.isFileID())
+    return NULL;
   
   { // Look into our cache of summaries to see if we have already computed
     // a summary for this FunctionDecl.
@@ -148,12 +216,169 @@
       return I->second;
   }
   
-  //
+#if 0
+  SourceManager& SrcMgr = Ctx.getSourceManager();
+  unsigned fid = Loc.getFileID();
+  const FileEntry* FE = SrcMgr.getFileEntryForID(fid);
   
+  if (!FE)
+    return NULL;
+  
+  const char* DirName = FE->getDir()->getName();  
+  assert (DirName);
+  assert (strlen(DirName) > 0);
+  
+  if (!strstr(DirName, "CoreFoundation")) {
+    SummaryMap[FD] = NULL;
+    return NULL;
+  }
+#endif
+  
+  const char* FName = FD->getIdentifier()->getName();
+    
+  if (FName[0] == 'C' && FName[1] == 'F') {
+    CFRefSummary* S = getCFSummary(FD, FName);
+    SummaryMap[FD] = S;
+    return S;
+  }
   
   return NULL;  
 }
 
+CFRefSummary* CFRefSummaryManager::getCFSummary(FunctionDecl* FD,
+                                                const char* FName) {
+  
+  // For now, only generate summaries for functions that have a prototype.
+  
+  FunctionTypeProto* FT =
+    dyn_cast<FunctionTypeProto>(FD->getType().getTypePtr());
+  
+  if (!FT)
+    return NULL;
+  
+  FName += 2;
+
+  if (strcmp(FName, "Retain") == 0)
+    return getCannedCFSummary(FT, true);
+  
+  if (strcmp(FName, "Release") == 0)
+    return getCannedCFSummary(FT, false);
+  
+  assert (ScratchArgs.empty());
+  bool usesCreateRule = false;
+  
+  if (strstr(FName, "Create"))
+    usesCreateRule = true;
+  
+  if (!usesCreateRule && strstr(FName, "Copy"))
+    usesCreateRule = true;
+  
+  if (usesCreateRule)
+    return getCFSummaryCreateRule(FT);
+
+  if (strstr(FName, "Get"))
+    return getCFSummaryGetRule(FT);
+  
+  return NULL;
+}
+
+CFRefSummary* CFRefSummaryManager::getCannedCFSummary(FunctionTypeProto* FT,
+                                                      bool isRetain) {
+  
+  if (FT->getNumArgs() != 1)
+    return NULL;
+  
+  TypedefType* ArgT = dyn_cast<TypedefType>(FT->getArgType(0).getTypePtr());
+  
+  if (!ArgT)
+    return NULL;
+  
+  // For CFRetain/CFRelease, the first (and only) argument is of type 
+  // "CFTypeRef".
+  
+  const char* TDName = ArgT->getDecl()->getIdentifier()->getName();
+  assert (TDName);
+  
+  if (strcmp("CFTypeRef", TDName) == 0)
+    return NULL;
+  
+  if (!ArgT->isPointerType())
+    return NULL;
+  
+  // Check the return type.  It should also be "CFTypeRef".
+  
+  QualType RetTy = FT->getResultType();
+  
+  if (RetTy.getTypePtr() != ArgT)
+    return NULL;
+  
+  // The function's interface checks out.  Generate a canned summary.
+  
+  assert (ScratchArgs.empty());
+  ScratchArgs.push_back(isRetain ? IncRef : DecRef);
+  
+  return getPersistentSummary(getArgEffects(), RetEffect::MakeAlias(0));
+}
+
+static bool isCFRefType(QualType T) {
+  
+  if (!T->isPointerType())
+    return false;
+  
+  // Check the typedef for the name "CF" and the substring "Ref".
+  
+  TypedefType* TD = dyn_cast<TypedefType>(T.getTypePtr());
+  
+  if (!TD)
+    return false;
+  
+  const char* TDName = TD->getDecl()->getIdentifier()->getName();
+  assert (TDName);
+  
+  if (TDName[0] != 'C' || TDName[1] != 'F')
+    return false;
+  
+  if (strstr(TDName, "Ref") == 0)
+    return false;
+  
+  return true;
+}
+  
+
+CFRefSummary*
+CFRefSummaryManager::getCFSummaryCreateRule(FunctionTypeProto* FT) {
+ 
+  if (!isCFRefType(FT->getResultType()))
+    return NULL;
+
+  assert (ScratchArgs.empty());
+  
+  // FIXME: Add special-cases for functions that retain/release.  For now
+  //  just handle the default case.
+  
+  for (unsigned i = 0, n = FT->getNumArgs(); i != n; ++i)
+    ScratchArgs.push_back(DoNothing);
+  
+  return getPersistentSummary(getArgEffects(), RetEffect::MakeOwned());
+}
+
+CFRefSummary*
+CFRefSummaryManager::getCFSummaryGetRule(FunctionTypeProto* FT) {
+  
+  if (!isCFRefType(FT->getResultType()))
+    return NULL;
+  
+  assert (ScratchArgs.empty());
+  
+  // FIXME: Add special-cases for functions that retain/release.  For now
+  //  just handle the default case.
+  
+  for (unsigned i = 0, n = FT->getNumArgs(); i != n; ++i)
+    ScratchArgs.push_back(DoNothing);
+  
+  return getPersistentSummary(getArgEffects(), RetEffect::MakeNotOwned());
+}
+
 //===----------------------------------------------------------------------===//
 // Transfer functions.
 //===----------------------------------------------------------------------===//
@@ -246,8 +471,8 @@
   // Instance variables.
   
   CFRefSummaryManager Summaries;
-  RefBFactoryTy  RefBFactory;
-  
+  RefBFactoryTy       RefBFactory;
+     
   UseAfterReleasesTy UseAfterReleases;
   ReleasesNotOwnedTy ReleasesNotOwned;
   
@@ -282,9 +507,8 @@
   // Calls.
   
   virtual void EvalCall(ExplodedNodeSet<ValueState>& Dst,
-                        ValueStateManager& StateMgr,
+                        GRExprEngine& Engine,
                         GRStmtNodeBuilder<ValueState>& Builder,
-                        BasicValueFactory& BasicVals,
                         CallExpr* CE, LVal L,
                         ExplodedNode<ValueState>* Pred);  
 };
@@ -307,11 +531,12 @@
 }
 
 void CFRefCount::EvalCall(ExplodedNodeSet<ValueState>& Dst,
-                            ValueStateManager& StateMgr,
-                            GRStmtNodeBuilder<ValueState>& Builder,
-                            BasicValueFactory& BasicVals,
-                            CallExpr* CE, LVal L,
-                            ExplodedNode<ValueState>* Pred) {
+                          GRExprEngine& Engine,
+                          GRStmtNodeBuilder<ValueState>& Builder,
+                          CallExpr* CE, LVal L,
+                          ExplodedNode<ValueState>* Pred) {
+  
+  ValueStateManager& StateMgr = Engine.getStateManager();
   
   // FIXME: Support calls to things other than lval::FuncVal.  At the very
   //  least we should stop tracking ref-state for ref-counted objects passed
@@ -323,7 +548,7 @@
 
   lval::FuncVal FV = cast<lval::FuncVal>(L);
   FunctionDecl* FD = FV.getDecl();
-  CFRefSummary* Summ = Summaries.getSummary(FD);
+  CFRefSummary* Summ = Summaries.getSummary(FD, Engine.getContext());
 
   // Get the state.
   
@@ -355,35 +580,36 @@
             
       if (isa<LVal>(V))
         StateMgr.Unbind(StVals, cast<LVal>(V));
-    }    
+    }
+    
+    St = StateMgr.getPersistentState(StVals);
+    Builder.Nodify(Dst, CE, Pred, St);
+    return;
   }
-  else {
+  
+  // This function has a summary.  Evaluate the effect of the arguments.
+  
+  unsigned idx = 0;
+  
+  for (CallExpr::arg_iterator I=CE->arg_begin(), E=CE->arg_end();
+        I!=E; ++I, ++idx) {
     
-    // This function has a summary.  Evaluate the effect of the arguments.
+    RVal V = StateMgr.GetRVal(St, *I);
     
-    unsigned idx = 0;
-    
-    for (CallExpr::arg_iterator I=CE->arg_begin(), E=CE->arg_end();
-          I!=E; ++I, ++idx) {
-      
-      RVal V = StateMgr.GetRVal(St, *I);
-      
-      if (isa<lval::SymbolVal>(V)) {
-        SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
-        RefBindings B = GetRefBindings(StVals);
+    if (isa<lval::SymbolVal>(V)) {
+      SymbolID Sym = cast<lval::SymbolVal>(V).getSymbol();
+      RefBindings B = GetRefBindings(StVals);
 
-        if (RefBindings::TreeTy* T = B.SlimFind(Sym)) {
-          B = Update(B, Sym, T->getValue().second, Summ->getArg(idx), hasError);
-          SetRefBindings(StVals, B);
-          if (hasError) break;
-        }
+      if (RefBindings::TreeTy* T = B.SlimFind(Sym)) {
+        B = Update(B, Sym, T->getValue().second, Summ->getArg(idx), hasError);
+        SetRefBindings(StVals, B);
+        if (hasError) break;
       }
-    }    
-  }
-  
-  St = StateMgr.getPersistentState(StVals);
-  
+    }
+  }    
+    
   if (hasError) {
+    St = StateMgr.getPersistentState(StVals);
     GRExprEngine::NodeTy* N = Builder.generateNode(CE, St, Pred);
 
     if (N) {
@@ -399,10 +625,61 @@
           ReleasesNotOwned.insert(N);
           break;
       }
-    }    
+    }
+    
+    return;
   }
-  else
-    Builder.Nodify(Dst, CE, Pred, St);
+
+  // Finally, consult the summary for the return value.
+  
+  RetEffect RE = Summ->getRet();
+  St = StateMgr.getPersistentState(StVals);
+
+  
+  switch (RE.getKind()) {
+    default:
+      assert (false && "Unhandled RetEffect."); break;
+    
+    case RetEffect::Alias: {
+      unsigned idx = RE.getValue();
+      assert (idx < CE->getNumArgs());
+      RVal V = StateMgr.GetRVal(St, CE->getArg(idx));
+      St = StateMgr.SetRVal(St, CE, V, Engine.getCFG().isBlkExpr(CE), false);
+      break;
+    }
+      
+    case RetEffect::OwnedSymbol: {
+      unsigned Count = Builder.getCurrentBlockCount();
+      SymbolID Sym = Engine.getSymbolManager().getCallRetValSymbol(CE, Count);
+
+      ValueState StImpl = *St;
+      RefBindings B = GetRefBindings(StImpl);
+      SetRefBindings(StImpl, RefBFactory.Add(B, Sym, RefVal::makeOwned(1)));
+      
+      St = StateMgr.SetRVal(StateMgr.getPersistentState(StImpl),
+                            CE, lval::SymbolVal(Sym),
+                            Engine.getCFG().isBlkExpr(CE), false);
+      
+      break;
+    }
+      
+    case RetEffect::NotOwnedSymbol: {
+      unsigned Count = Builder.getCurrentBlockCount();
+      SymbolID Sym = Engine.getSymbolManager().getCallRetValSymbol(CE, Count);
+      
+      ValueState StImpl = *St;
+      RefBindings B = GetRefBindings(StImpl);
+      SetRefBindings(StImpl, RefBFactory.Add(B, Sym, RefVal::makeNotOwned()));
+      
+      St = StateMgr.SetRVal(StateMgr.getPersistentState(StImpl),
+                            CE, lval::SymbolVal(Sym),
+                            Engine.getCFG().isBlkExpr(CE), false);
+      
+      break;
+    }
+  }
+      
+  Builder.Nodify(Dst, CE, Pred, St);
 }
 
 
@@ -418,6 +695,12 @@
       assert (false && "Unhandled CFRef transition.");
       
     case DoNothing:
+      if (V.getKind() == RefVal::Released) {
+        V = RefVal::makeUseAfterRelease();        
+        hasError = V.getKind();
+        break;
+      }
+      
       return B;
       
     case IncRef: