[cfe-commits] r159692 - in /cfe/trunk: include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp lib/StaticAnalyzer/Core/Calls.cpp lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp test/Analysis/operator-calls.cpp

Jordan Rose jordan_rose at apple.com
Tue Jul 3 15:55:57 PDT 2012


Author: jrose
Date: Tue Jul  3 17:55:57 2012
New Revision: 159692

URL: http://llvm.org/viewvc/llvm-project?rev=159692&view=rev
Log:
[analyzer] For now, don't inline non-static member overloaded operators.

Our current inlining support (specifically RegionStore::enterStackFrame)
doesn't know that calls to overloaded operators may be calls to non-static
member functions, and that in these cases the first argument should be
treated as 'this'. This caused incorrect results and sometimes crashes.

The long-term fix will be to rewrite RegionStore::enterStackFrame to use
CallEvent and its subclasses, but for now we can just disable these
problematic calls by classifying them under a new CallEvent,
CXXMemberOperatorCall.

Modified:
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h
    cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/Calls.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
    cfe/trunk/test/Analysis/operator-calls.cpp

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h?rev=159692&r1=159691&r2=159692&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/Calls.h Tue Jul  3 17:55:57 2012
@@ -29,6 +29,7 @@
 enum CallEventKind {
   CE_Function,
   CE_CXXMember,
+  CE_CXXMemberOperator,
   CE_Block,
   CE_BEG_SIMPLE_CALLS = CE_Function,
   CE_END_SIMPLE_CALLS = CE_Block,
@@ -264,9 +265,36 @@
   }
 };
 
+/// \brief Represents a C++ overloaded operator call where the operator is
+/// implemented as a non-static member function.
+///
+/// Example: <tt>iter + 1</tt>
+class CXXMemberOperatorCall : public SimpleCall {
+protected:
+  void addExtraInvalidatedRegions(RegionList &Regions) const;
+
+public:
+  CXXMemberOperatorCall(const CXXOperatorCallExpr *CE, ProgramStateRef St,
+                        const LocationContext *LCtx)
+    : SimpleCall(CE, St, LCtx, CE_CXXMemberOperator) {}
+
+  const CXXOperatorCallExpr *getOriginExpr() const {
+    return cast<CXXOperatorCallExpr>(SimpleCall::getOriginExpr());
+  }
+
+  unsigned getNumArgs() const { return getOriginExpr()->getNumArgs() - 1; }
+  const Expr *getArgExpr(unsigned Index) const {
+    return getOriginExpr()->getArg(Index + 1);
+  }
+
+  static bool classof(const CallEvent *CA) {
+    return CA->getKind() == CE_CXXMemberOperator;
+  }
+};
+
 /// \brief Represents a call to a block.
 ///
-/// Example: \c ^{ /* ... */ }()
+/// Example: <tt>^{ /* ... */ }()</tt>
 class BlockCall : public SimpleCall {
 protected:
   void addExtraInvalidatedRegions(RegionList &Regions) const;

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp?rev=159692&r1=159691&r2=159692&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp Tue Jul  3 17:55:57 2012
@@ -945,6 +945,7 @@
     Summ = getFunctionSummary(cast<FunctionCall>(Call).getDecl());
     break;
   case CE_CXXMember:
+  case CE_CXXMemberOperator:
   case CE_Block:
   case CE_CXXConstructor:
   case CE_CXXAllocator:

Modified: cfe/trunk/lib/StaticAnalyzer/Core/Calls.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/Calls.cpp?rev=159692&r1=159691&r2=159692&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/Calls.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/Calls.cpp Tue Jul  3 17:55:57 2012
@@ -303,6 +303,14 @@
 }
 
 
+void
+CXXMemberOperatorCall::addExtraInvalidatedRegions(RegionList &Regions) const {
+  const Expr *Base = getOriginExpr()->getArg(0);
+  if (const MemRegion *R = getSVal(Base).getAsRegion())
+    Regions.push_back(R);
+}
+
+
 const BlockDataRegion *BlockCall::getBlockRegion() const {
   const Expr *Callee = getOriginExpr()->getCallee();
   const MemRegion *DataReg = getSVal(Callee).getAsRegion();

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp?rev=159692&r1=159691&r2=159692&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp Tue Jul  3 17:55:57 2012
@@ -22,6 +22,23 @@
 using namespace clang;
 using namespace ento;
 
+static CallEventKind classifyCallExpr(const CallExpr *CE) {
+  if (isa<CXXMemberCallExpr>(CE))
+    return CE_CXXMember;
+
+  const CXXOperatorCallExpr *OpCE = dyn_cast<CXXOperatorCallExpr>(CE);
+  if (OpCE) {
+    const FunctionDecl *DirectCallee = CE->getDirectCallee();
+    if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DirectCallee))
+      if (MD->isInstance())
+        return CE_CXXMemberOperator;
+  } else if (CE->getCallee()->getType()->isBlockPointerType()) {
+    return CE_Block;
+  }
+
+  return CE_Function;
+}
+
 void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
   // Get the entry block in the CFG of the callee.
   const StackFrameContext *calleeCtx = CE.getCalleeContext();
@@ -265,6 +282,12 @@
   case CE_CXXMember:
     // These are always at least possible to inline.
     break;
+  case CE_CXXMemberOperator:
+    // FIXME: This should be possible to inline, but
+    // RegionStore::enterStackFrame isn't smart enough to handle the first
+    // argument being 'this'. The correct solution is to use CallEvent in
+    // enterStackFrame as well.
+    return false;
   case CE_CXXConstructor:
     // Do not inline constructors until we can model destructors.
     // This is unfortunate, but basically necessary for smart pointers and such.
@@ -336,9 +359,7 @@
   getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, CE, *this);
 
   // Get the callee kind.
-  const CXXMemberCallExpr *MemberCE = dyn_cast<CXXMemberCallExpr>(CE);
-  bool IsBlock = (MemberCE ? false
-                           : CE->getCallee()->getType()->isBlockPointerType());
+  CallEventKind K = classifyCallExpr(CE);
 
   // Evaluate the function call.  We try each of the checkers
   // to see if the can evaluate the function call.
@@ -349,12 +370,25 @@
     const LocationContext *LCtx = (*I)->getLocationContext();
 
     // Evaluate the call.
-    if (MemberCE)
-      evalCall(dstCallEvaluated, *I, CXXMemberCall(MemberCE, State, LCtx));
-    else if (IsBlock)
-      evalCall(dstCallEvaluated, *I, BlockCall(CE, State, LCtx));
-    else
+    switch (K) {
+    case CE_Function:
       evalCall(dstCallEvaluated, *I, FunctionCall(CE, State, LCtx));
+      break;
+    case CE_CXXMember:
+      evalCall(dstCallEvaluated, *I, CXXMemberCall(cast<CXXMemberCallExpr>(CE),
+                                                   State, LCtx));
+      break;
+    case CE_CXXMemberOperator:
+      evalCall(dstCallEvaluated, *I,
+               CXXMemberOperatorCall(cast<CXXOperatorCallExpr>(CE),
+                                     State, LCtx));
+      break;
+    case CE_Block:
+      evalCall(dstCallEvaluated, *I, BlockCall(CE, State, LCtx));
+      break;
+    default:
+      llvm_unreachable("Non-CallExpr CallEventKind");
+    }
   }
 
   // Finally, perform the post-condition check of the CallExpr and store

Modified: cfe/trunk/test/Analysis/operator-calls.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/operator-calls.cpp?rev=159692&r1=159691&r2=159692&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/operator-calls.cpp (original)
+++ cfe/trunk/test/Analysis/operator-calls.cpp Tue Jul  3 17:55:57 2012
@@ -1,4 +1,6 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,experimental.core,debug.ExprInspection -verify %s
+void clang_analyzer_eval(bool);
+
 struct X0 { };
 bool operator==(const X0&, const X0&);
 
@@ -14,3 +16,18 @@
 bool PR7287(X0 a, X0 b) {
   return operator==(a, b);
 }
+
+
+// Inlining non-static member operators mistakenly treated 'this' as the first
+// argument for a while.
+
+struct IntComparable {
+  bool operator==(int x) const {
+    return x == 0;
+  }
+};
+
+void testMemberOperator(IntComparable B) {
+  // FIXME: Change this to TRUE when we re-enable inlining.
+  clang_analyzer_eval(B == 0); // expected-warning{{UNKNOWN}}
+}





More information about the cfe-commits mailing list