r289873 - [analyzer] Add a new SVal to support pointer-to-member operations.

Devin Coughlin via cfe-commits cfe-commits at lists.llvm.org
Thu Dec 15 13:27:06 PST 2016


Author: dcoughlin
Date: Thu Dec 15 15:27:06 2016
New Revision: 289873

URL: http://llvm.org/viewvc/llvm-project?rev=289873&view=rev
Log:
[analyzer] Add a new SVal to support pointer-to-member operations.

Add a new type of NonLoc SVal for C++ pointer-to-member operations. This SVal
supports both pointers to member functions and pointers to member data.

A patch by Kirill Romanenkov!

Differential Revision: https://reviews.llvm.org/D25475

Modified:
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
    cfe/trunk/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/SValBuilder.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/SVals.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
    cfe/trunk/test/Analysis/pointer-to-member.cpp

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h?rev=289873&r1=289872&r2=289873&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h Thu Dec 15 15:27:06 2016
@@ -59,6 +59,29 @@ public:
   void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, store, region); }
 };
 
+class PointerToMemberData: public llvm::FoldingSetNode {
+  const DeclaratorDecl *D;
+  llvm::ImmutableList<const CXXBaseSpecifier *> L;
+
+public:
+  PointerToMemberData(const DeclaratorDecl *D,
+                      llvm::ImmutableList<const CXXBaseSpecifier *> L)
+    : D(D), L(L) {}
+
+  typedef llvm::ImmutableList<const CXXBaseSpecifier *>::iterator iterator;
+  iterator begin() const { return L.begin(); }
+  iterator end() const { return L.end(); }
+
+  static void Profile(llvm::FoldingSetNodeID& ID, const DeclaratorDecl *D,
+                      llvm::ImmutableList<const CXXBaseSpecifier *> L);
+
+  void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, D, L); }
+  const DeclaratorDecl *getDeclaratorDecl() const {return D;}
+  llvm::ImmutableList<const CXXBaseSpecifier *> getCXXBaseList() const {
+    return L;
+  }
+};
+
 class BasicValueFactory {
   typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<llvm::APSInt> >
           APSIntSetTy;
@@ -71,8 +94,10 @@ class BasicValueFactory {
   void *        PersistentSValPairs;
 
   llvm::ImmutableList<SVal>::Factory SValListFactory;
+  llvm::ImmutableList<const CXXBaseSpecifier*>::Factory CXXBaseListFactory;
   llvm::FoldingSet<CompoundValData>  CompoundValDataSet;
   llvm::FoldingSet<LazyCompoundValData> LazyCompoundValDataSet;
+  llvm::FoldingSet<PointerToMemberData> PointerToMemberDataSet;
 
   // This is private because external clients should use the factory
   // method that takes a QualType.
@@ -81,7 +106,8 @@ class BasicValueFactory {
 public:
   BasicValueFactory(ASTContext &ctx, llvm::BumpPtrAllocator &Alloc)
     : Ctx(ctx), BPAlloc(Alloc), PersistentSVals(nullptr),
-      PersistentSValPairs(nullptr), SValListFactory(Alloc) {}
+      PersistentSValPairs(nullptr), SValListFactory(Alloc),
+      CXXBaseListFactory(Alloc) {}
 
   ~BasicValueFactory();
 
@@ -172,14 +198,32 @@ public:
   const LazyCompoundValData *getLazyCompoundValData(const StoreRef &store,
                                             const TypedValueRegion *region);
 
+  const PointerToMemberData *getPointerToMemberData(
+      const DeclaratorDecl *DD,
+      llvm::ImmutableList<const CXXBaseSpecifier *> L);
+
   llvm::ImmutableList<SVal> getEmptySValList() {
     return SValListFactory.getEmptyList();
   }
 
-  llvm::ImmutableList<SVal> consVals(SVal X, llvm::ImmutableList<SVal> L) {
+  llvm::ImmutableList<SVal> prependSVal(SVal X, llvm::ImmutableList<SVal> L) {
     return SValListFactory.add(X, L);
   }
 
+  llvm::ImmutableList<const CXXBaseSpecifier *> getEmptyCXXBaseList() {
+    return CXXBaseListFactory.getEmptyList();
+  }
+
+  llvm::ImmutableList<const CXXBaseSpecifier *> prependCXXBase(
+      const CXXBaseSpecifier *CBS,
+      llvm::ImmutableList<const CXXBaseSpecifier *> L) {
+    return CXXBaseListFactory.add(CBS, L);
+  }
+
+  const clang::ento::PointerToMemberData *accumCXXBase(
+      llvm::iterator_range<CastExpr::path_const_iterator> PathRange,
+      const nonloc::PointerToMember &PTM);
+
   const llvm::APSInt* evalAPSInt(BinaryOperator::Opcode Op,
                                      const llvm::APSInt& V1,
                                      const llvm::APSInt& V2);

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h?rev=289873&r1=289872&r2=289873&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h Thu Dec 15 15:27:06 2016
@@ -479,6 +479,22 @@ public:
     return X.isValid() ? svalBuilder.evalComplement(X.castAs<NonLoc>()) : X;
   }
 
+  ProgramStateRef handleLValueBitCast(ProgramStateRef state, const Expr *Ex,
+                                      const LocationContext *LCtx, QualType T,
+                                      QualType ExTy, const CastExpr *CastE,
+                                      StmtNodeBuilder &Bldr,
+                                      ExplodedNode *Pred);
+
+  ProgramStateRef handleLVectorSplat(ProgramStateRef state,
+                                     const LocationContext *LCtx,
+                                     const CastExpr *CastE,
+                                     StmtNodeBuilder &Bldr,
+                                     ExplodedNode *Pred);
+
+  void handleUOExtension(ExplodedNodeSet::iterator I,
+                         const UnaryOperator* U,
+                         StmtNodeBuilder &Bldr);
+
 public:
 
   SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op,

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h?rev=289873&r1=289872&r2=289873&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h Thu Dec 15 15:27:06 2016
@@ -204,6 +204,8 @@ public:
                                    const LocationContext *LCtx,
                                    unsigned count);
 
+  DefinedSVal getMemberPointer(const DeclaratorDecl *DD);
+
   DefinedSVal getFunctionPointer(const FunctionDecl *func);
   
   DefinedSVal getBlockPointer(const BlockDecl *block, CanQualType locTy,
@@ -226,6 +228,14 @@ public:
         BasicVals.getLazyCompoundValData(store, region));
   }
 
+  NonLoc makePointerToMember(const DeclaratorDecl *DD) {
+    return nonloc::PointerToMember(DD);
+  }
+
+  NonLoc makePointerToMember(const PointerToMemberData *PTMD) {
+    return nonloc::PointerToMember(PTMD);
+  }
+
   NonLoc makeZeroArrayIndex() {
     return nonloc::ConcreteInt(BasicVals.getValue(0, ArrayIndexTy));
   }

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def?rev=289873&r1=289872&r2=289873&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def Thu Dec 15 15:27:06 2016
@@ -66,6 +66,7 @@ ABSTRACT_SVAL(DefinedOrUnknownSVal, SVal
       NONLOC_SVAL(LazyCompoundVal, NonLoc)
       NONLOC_SVAL(LocAsInteger, NonLoc)
       NONLOC_SVAL(SymbolVal, NonLoc)
+      NONLOC_SVAL(PointerToMember, NonLoc)
 
 #undef NONLOC_SVAL
 #undef LOC_SVAL

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h?rev=289873&r1=289872&r2=289873&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h Thu Dec 15 15:27:06 2016
@@ -32,6 +32,7 @@ namespace ento {
 
 class CompoundValData;
 class LazyCompoundValData;
+class PointerToMemberData;
 class ProgramState;
 class BasicValueFactory;
 class MemRegion;
@@ -459,6 +460,51 @@ private:
   }
 };
 
+/// \brief Value representing pointer-to-member.
+///
+/// This value is qualified as NonLoc because neither loading nor storing
+/// operations are aplied to it. Instead, the analyzer uses the L-value coming
+/// from pointer-to-member applied to an object.
+/// This SVal is represented by a DeclaratorDecl which can be a member function
+/// pointer or a member data pointer and a list of CXXBaseSpecifiers. This list
+/// is required to accumulate the pointer-to-member cast history to figure out
+/// the correct subobject field.
+class PointerToMember : public NonLoc {
+  friend class ento::SValBuilder;
+
+public:
+  typedef llvm::PointerUnion<const DeclaratorDecl *,
+                             const PointerToMemberData *> PTMDataType;
+  const PTMDataType getPTMData() const {
+    return PTMDataType::getFromOpaqueValue(const_cast<void *>(Data));
+  }
+  bool isNullMemberPointer() const {
+    return getPTMData().isNull();
+  }
+  const DeclaratorDecl *getDecl() const;
+  template<typename AdjustedDecl>
+  const AdjustedDecl* getDeclAs() const {
+    return dyn_cast_or_null<AdjustedDecl>(getDecl());
+  }
+  typedef llvm::ImmutableList<const CXXBaseSpecifier *>::iterator iterator;
+  iterator begin() const;
+  iterator end() const;
+
+private:
+  explicit PointerToMember(const PTMDataType D)
+    : NonLoc(PointerToMemberKind, D.getOpaqueValue()) {}
+  friend class SVal;
+  PointerToMember() {}
+  static bool isKind(const SVal& V) {
+    return V.getBaseKind() == NonLocKind &&
+           V.getSubKind() == PointerToMemberKind;
+  }
+
+  static bool isKind(const NonLoc& V) {
+    return V.getSubKind() == PointerToMemberKind;
+  }
+};
+
 } // end namespace ento::nonloc
 
 //==------------------------------------------------------------------------==//

Modified: cfe/trunk/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/BasicValueFactory.cpp?rev=289873&r1=289872&r2=289873&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/BasicValueFactory.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/BasicValueFactory.cpp Thu Dec 15 15:27:06 2016
@@ -33,6 +33,13 @@ void LazyCompoundValData::Profile(llvm::
   ID.AddPointer(region);
 }
 
+void PointerToMemberData::Profile(
+    llvm::FoldingSetNodeID& ID, const DeclaratorDecl *D,
+    llvm::ImmutableList<const CXXBaseSpecifier *> L) {
+  ID.AddPointer(D);
+  ID.AddPointer(L.getInternalPointer());
+}
+
 typedef std::pair<SVal, uintptr_t> SValData;
 typedef std::pair<SVal, SVal> SValPair;
 
@@ -142,6 +149,49 @@ BasicValueFactory::getLazyCompoundValDat
   return D;
 }
 
+const PointerToMemberData *BasicValueFactory::getPointerToMemberData(
+    const DeclaratorDecl *DD, llvm::ImmutableList<const CXXBaseSpecifier*> L) {
+  llvm::FoldingSetNodeID ID;
+  PointerToMemberData::Profile(ID, DD, L);
+  void *InsertPos;
+
+  PointerToMemberData *D =
+      PointerToMemberDataSet.FindNodeOrInsertPos(ID, InsertPos);
+
+  if (!D) {
+    D = (PointerToMemberData*) BPAlloc.Allocate<PointerToMemberData>();
+    new (D) PointerToMemberData(DD, L);
+    PointerToMemberDataSet.InsertNode(D, InsertPos);
+  }
+
+  return D;
+}
+
+const clang::ento::PointerToMemberData *BasicValueFactory::accumCXXBase(
+    llvm::iterator_range<CastExpr::path_const_iterator> PathRange,
+    const nonloc::PointerToMember &PTM) {
+  nonloc::PointerToMember::PTMDataType PTMDT = PTM.getPTMData();
+  const DeclaratorDecl *DD = nullptr;
+  llvm::ImmutableList<const CXXBaseSpecifier *> PathList;
+
+  if (PTMDT.isNull() || PTMDT.is<const DeclaratorDecl *>()) {
+    if (PTMDT.is<const DeclaratorDecl *>())
+      DD = PTMDT.get<const DeclaratorDecl *>();
+
+    PathList = CXXBaseListFactory.getEmptyList();
+  } else { // const PointerToMemberData *
+    const PointerToMemberData *PTMD =
+        PTMDT.get<const PointerToMemberData *>();
+    DD = PTMD->getDeclaratorDecl();
+
+    PathList = PTMD->getCXXBaseList();
+  }
+
+  for (const auto &I : llvm::reverse(PathRange))
+    PathList = prependCXXBase(I, PathList);
+  return getPointerToMemberData(DD, PathList);
+}
+
 const llvm::APSInt*
 BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op,
                              const llvm::APSInt& V1, const llvm::APSInt& V2) {

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp?rev=289873&r1=289872&r2=289873&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineC.cpp Thu Dec 15 15:27:06 2016
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
 
@@ -246,6 +247,38 @@ void ExprEngine::VisitBlockExpr(const Bl
   getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this);
 }
 
+ProgramStateRef ExprEngine::handleLValueBitCast(
+    ProgramStateRef state, const Expr* Ex, const LocationContext* LCtx,
+    QualType T, QualType ExTy, const CastExpr* CastE, StmtNodeBuilder& Bldr,
+    ExplodedNode* Pred) {
+  // Delegate to SValBuilder to process.
+  SVal V = state->getSVal(Ex, LCtx);
+  V = svalBuilder.evalCast(V, T, ExTy);
+  // Negate the result if we're treating the boolean as a signed i1
+  if (CastE->getCastKind() == CK_BooleanToSignedIntegral)
+    V = evalMinus(V);
+  state = state->BindExpr(CastE, LCtx, V);
+  Bldr.generateNode(CastE, Pred, state);
+
+  return state;
+}
+
+ProgramStateRef ExprEngine::handleLVectorSplat(
+    ProgramStateRef state, const LocationContext* LCtx, const CastExpr* CastE,
+    StmtNodeBuilder &Bldr, ExplodedNode* Pred) {
+  // Recover some path sensitivity by conjuring a new value.
+  QualType resultType = CastE->getType();
+  if (CastE->isGLValue())
+    resultType = getContext().getPointerType(resultType);
+  SVal result = svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx,
+                                             resultType,
+                                             currBldrCtx->blockCount());
+  state = state->BindExpr(CastE, LCtx, result);
+  Bldr.generateNode(CastE, Pred, state);
+
+  return state;
+}
+
 void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
                            ExplodedNode *Pred, ExplodedNodeSet &Dst) {
 
@@ -310,8 +343,21 @@ void ExprEngine::VisitCast(const CastExp
         continue;
       }
       case CK_MemberPointerToBoolean:
-        // FIXME: For now, member pointers are represented by void *.
-        // FALLTHROUGH
+      case CK_PointerToBoolean: {
+        SVal V = state->getSVal(Ex, LCtx);
+        auto PTMSV = V.getAs<nonloc::PointerToMember>();
+        if (PTMSV)
+          V = svalBuilder.makeTruthVal(!PTMSV->isNullMemberPointer(), ExTy);
+        if (V.isUndef() || PTMSV) {
+          state = state->BindExpr(CastE, LCtx, V);
+          Bldr.generateNode(CastE, Pred, state);
+          continue;
+        }
+        // Explicitly proceed with default handler for this case cascade.
+        state =
+            handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred);
+        continue;
+      }
       case CK_Dependent:
       case CK_ArrayToPointerDecay:
       case CK_BitCast:
@@ -319,8 +365,18 @@ void ExprEngine::VisitCast(const CastExp
       case CK_BooleanToSignedIntegral:
       case CK_NullToPointer:
       case CK_IntegralToPointer:
-      case CK_PointerToIntegral:
-      case CK_PointerToBoolean:
+      case CK_PointerToIntegral: {
+        SVal V = state->getSVal(Ex, LCtx);
+        if (V.getAs<nonloc::PointerToMember>()) {
+          state = state->BindExpr(CastE, LCtx, UnknownVal());
+          Bldr.generateNode(CastE, Pred, state);
+          continue;
+        }
+        // Explicitly proceed with default handler for this case cascade.
+        state =
+            handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred);
+        continue;
+      }
       case CK_IntegralToBoolean:
       case CK_IntegralToFloating:
       case CK_FloatingToIntegral:
@@ -343,14 +399,8 @@ void ExprEngine::VisitCast(const CastExp
       case CK_ZeroToOCLEvent:
       case CK_IntToOCLSampler:
       case CK_LValueBitCast: {
-        // Delegate to SValBuilder to process.
-        SVal V = state->getSVal(Ex, LCtx);
-        V = svalBuilder.evalCast(V, T, ExTy);
-        // Negate the result if we're treating the boolean as a signed i1
-        if (CastE->getCastKind() == CK_BooleanToSignedIntegral)
-          V = evalMinus(V);
-        state = state->BindExpr(CastE, LCtx, V);
-        Bldr.generateNode(CastE, Pred, state);
+        state =
+            handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred);
         continue;
       }
       case CK_IntegralCast: {
@@ -435,27 +485,32 @@ void ExprEngine::VisitCast(const CastExp
         continue;
       }
       case CK_NullToMemberPointer: {
-        // FIXME: For now, member pointers are represented by void *.
-        SVal V = svalBuilder.makeNull();
+        SVal V = svalBuilder.getMemberPointer(nullptr);
         state = state->BindExpr(CastE, LCtx, V);
         Bldr.generateNode(CastE, Pred, state);
         continue;
       }
+      case CK_DerivedToBaseMemberPointer:
+      case CK_BaseToDerivedMemberPointer:
+      case CK_ReinterpretMemberPointer: {
+        SVal V = state->getSVal(Ex, LCtx);
+        if (auto PTMSV = V.getAs<nonloc::PointerToMember>()) {
+          SVal CastedPTMSV = svalBuilder.makePointerToMember(
+              getBasicVals().accumCXXBase(
+                  llvm::make_range<CastExpr::path_const_iterator>(
+                      CastE->path_begin(), CastE->path_end()), *PTMSV));
+          state = state->BindExpr(CastE, LCtx, CastedPTMSV);
+          Bldr.generateNode(CastE, Pred, state);
+          continue;
+        }
+        // Explicitly proceed with default handler for this case cascade.
+        state = handleLVectorSplat(state, LCtx, CastE, Bldr, Pred);
+        continue;
+      }
       // Various C++ casts that are not handled yet.
       case CK_ToUnion:
-      case CK_BaseToDerivedMemberPointer:
-      case CK_DerivedToBaseMemberPointer:
-      case CK_ReinterpretMemberPointer:
       case CK_VectorSplat: {
-        // Recover some path-sensitivty by conjuring a new value.
-        QualType resultType = CastE->getType();
-        if (CastE->isGLValue())
-          resultType = getContext().getPointerType(resultType);
-        SVal result = svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx,
-                                                   resultType,
-                                                   currBldrCtx->blockCount());
-        state = state->BindExpr(CastE, LCtx, result);
-        Bldr.generateNode(CastE, Pred, state);
+        state = handleLVectorSplat(state, LCtx, CastE, Bldr, Pred);
         continue;
       }
     }
@@ -658,7 +713,7 @@ void ExprEngine::VisitInitListExpr(const
     for (InitListExpr::const_reverse_iterator it = IE->rbegin(),
          ei = IE->rend(); it != ei; ++it) {
       SVal V = state->getSVal(cast<Expr>(*it), LCtx);
-      vals = getBasicVals().consVals(V, vals);
+      vals = getBasicVals().prependSVal(V, vals);
     }
 
     B.generateNode(IE, Pred,
@@ -803,8 +858,24 @@ VisitUnaryExprOrTypeTraitExpr(const Unar
   getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this);
 }
 
-void ExprEngine::VisitUnaryOperator(const UnaryOperator* U,
-                                    ExplodedNode *Pred,
+void ExprEngine::handleUOExtension(ExplodedNodeSet::iterator I,
+                                   const UnaryOperator *U,
+                                   StmtNodeBuilder &Bldr) {
+  // FIXME: We can probably just have some magic in Environment::getSVal()
+  // that propagates values, instead of creating a new node here.
+  //
+  // Unary "+" is a no-op, similar to a parentheses.  We still have places
+  // where it may be a block-level expression, so we need to
+  // generate an extra node that just propagates the value of the
+  // subexpression.
+  const Expr *Ex = U->getSubExpr()->IgnoreParens();
+  ProgramStateRef state = (*I)->getState();
+  const LocationContext *LCtx = (*I)->getLocationContext();
+  Bldr.generateNode(U, *I, state->BindExpr(U, LCtx,
+                                           state->getSVal(Ex, LCtx)));
+}
+
+void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
                                     ExplodedNodeSet &Dst) {
   // FIXME: Prechecks eventually go in ::Visit().
   ExplodedNodeSet CheckedSet;
@@ -856,24 +927,30 @@ void ExprEngine::VisitUnaryOperator(cons
       break;
     }
 
+    case UO_AddrOf: {
+      // Process pointer-to-member address operation.
+      const Expr *Ex = U->getSubExpr()->IgnoreParens();
+      if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Ex)) {
+        const ValueDecl *VD = DRE->getDecl();
+
+        if (isa<CXXMethodDecl>(VD) || isa<FieldDecl>(VD)) {
+          ProgramStateRef State = (*I)->getState();
+          const LocationContext *LCtx = (*I)->getLocationContext();
+          SVal SV = svalBuilder.getMemberPointer(cast<DeclaratorDecl>(VD));
+          Bldr.generateNode(U, *I, State->BindExpr(U, LCtx, SV));
+          break;
+        }
+      }
+      // Explicitly proceed with default handler for this case cascade.
+      handleUOExtension(I, U, Bldr);
+      break;
+    }
     case UO_Plus:
       assert(!U->isGLValue());
       // FALL-THROUGH.
     case UO_Deref:
-    case UO_AddrOf:
     case UO_Extension: {
-      // FIXME: We can probably just have some magic in Environment::getSVal()
-      // that propagates values, instead of creating a new node here.
-      //
-      // Unary "+" is a no-op, similar to a parentheses.  We still have places
-      // where it may be a block-level expression, so we need to
-      // generate an extra node that just propagates the value of the
-      // subexpression.
-      const Expr *Ex = U->getSubExpr()->IgnoreParens();
-      ProgramStateRef state = (*I)->getState();
-      const LocationContext *LCtx = (*I)->getLocationContext();
-      Bldr.generateNode(U, *I, state->BindExpr(U, LCtx,
-                                               state->getSVal(Ex, LCtx)));
+      handleUOExtension(I, U, Bldr);
       break;
     }
 

Modified: cfe/trunk/lib/StaticAnalyzer/Core/SValBuilder.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/SValBuilder.cpp?rev=289873&r1=289872&r2=289873&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/SValBuilder.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/SValBuilder.cpp Thu Dec 15 15:27:06 2016
@@ -217,6 +217,10 @@ SValBuilder::getDerivedRegionValueSymbol
   return nonloc::SymbolVal(sym);
 }
 
+DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl* DD) {
+  return nonloc::PointerToMember(DD);
+}
+
 DefinedSVal SValBuilder::getFunctionPointer(const FunctionDecl *func) {
   return loc::MemRegionVal(MemMgr.getFunctionCodeRegion(func));
 }

Modified: cfe/trunk/lib/StaticAnalyzer/Core/SVals.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/SVals.cpp?rev=289873&r1=289872&r2=289873&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/SVals.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/SVals.cpp Thu Dec 15 15:27:06 2016
@@ -16,6 +16,7 @@
 #include "clang/AST/ExprObjC.h"
 #include "clang/Basic/IdentifierTable.h"
 #include "llvm/Support/raw_ostream.h"
+#include "clang/AST/DeclCXX.h"
 using namespace clang;
 using namespace ento;
 using llvm::APSInt;
@@ -56,6 +57,10 @@ const FunctionDecl *SVal::getAsFunctionD
         return FD;
   }
 
+  if (auto X = getAs<nonloc::PointerToMember>()) {
+    if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(X->getDecl()))
+      return MD;
+  }
   return nullptr;
 }
 
@@ -155,6 +160,20 @@ const TypedValueRegion *nonloc::LazyComp
   return static_cast<const LazyCompoundValData*>(Data)->getRegion();
 }
 
+const DeclaratorDecl *nonloc::PointerToMember::getDecl() const {
+  const auto PTMD = this->getPTMData();
+  if (PTMD.isNull())
+    return nullptr;
+
+  const DeclaratorDecl *DD = nullptr;
+  if (PTMD.is<const DeclaratorDecl *>())
+    DD = PTMD.get<const DeclaratorDecl *>();
+  else
+    DD = PTMD.get<const PointerToMemberData *>()->getDeclaratorDecl();
+
+  return DD;
+}
+
 //===----------------------------------------------------------------------===//
 // Other Iterators.
 //===----------------------------------------------------------------------===//
@@ -167,6 +186,20 @@ nonloc::CompoundVal::iterator nonloc::Co
   return getValue()->end();
 }
 
+nonloc::PointerToMember::iterator nonloc::PointerToMember::begin() const {
+  const PTMDataType PTMD = getPTMData();
+  if (PTMD.is<const DeclaratorDecl *>())
+    return nonloc::PointerToMember::iterator();
+  return PTMD.get<const PointerToMemberData *>()->begin();
+}
+
+nonloc::PointerToMember::iterator nonloc::PointerToMember::end() const {
+  const PTMDataType PTMD = getPTMData();
+  if (PTMD.is<const DeclaratorDecl *>())
+    return nonloc::PointerToMember::iterator();
+  return PTMD.get<const PointerToMemberData *>()->end();
+}
+
 //===----------------------------------------------------------------------===//
 // Useful predicates.
 //===----------------------------------------------------------------------===//
@@ -299,6 +332,26 @@ void NonLoc::dumpToStream(raw_ostream &o
          << '}';
       break;
     }
+    case nonloc::PointerToMemberKind: {
+      os << "pointerToMember{";
+      const nonloc::PointerToMember &CastRes =
+          castAs<nonloc::PointerToMember>();
+      if (CastRes.getDecl())
+        os << "|" << CastRes.getDecl()->getQualifiedNameAsString() << "|";
+      bool first = true;
+      for (const auto &I : CastRes) {
+        if (first) {
+          os << ' '; first = false;
+        }
+        else
+          os << ", ";
+
+        os << (*I).getType().getAsString();
+      }
+
+      os << '}';
+      break;
+    }
     default:
       assert (false && "Pretty-printed not implemented for this NonLoc.");
       break;

Modified: cfe/trunk/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp?rev=289873&r1=289872&r2=289873&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp Thu Dec 15 15:27:06 2016
@@ -182,6 +182,12 @@ ProgramStateRef SimpleConstraintManager:
     return isFeasible ? State : nullptr;
   }
 
+  case nonloc::PointerToMemberKind: {
+    bool IsNull = !Cond.castAs<nonloc::PointerToMember>().isNullMemberPointer();
+    bool IsFeasible = IsNull ? Assumption : !Assumption;
+    return IsFeasible ? State : nullptr;
+  }
+
   case nonloc::LocAsIntegerKind:
     return assume(State, Cond.castAs<nonloc::LocAsInteger>().getLoc(),
                   Assumption);

Modified: cfe/trunk/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp?rev=289873&r1=289872&r2=289873&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp Thu Dec 15 15:27:06 2016
@@ -69,6 +69,9 @@ SVal SimpleSValBuilder::evalCastFromNonL
 
   bool isLocType = Loc::isLocType(castTy);
 
+  if (val.getAs<nonloc::PointerToMember>())
+    return val;
+
   if (Optional<nonloc::LocAsInteger> LI = val.getAs<nonloc::LocAsInteger>()) {
     if (isLocType)
       return LI->getLoc();
@@ -335,6 +338,21 @@ SVal SimpleSValBuilder::evalBinOpNN(Prog
     switch (lhs.getSubKind()) {
     default:
       return makeSymExprValNN(state, op, lhs, rhs, resultTy);
+    case nonloc::PointerToMemberKind: {
+      assert(rhs.getSubKind() == nonloc::PointerToMemberKind &&
+             "Both SVals should have pointer-to-member-type");
+      auto LPTM = lhs.castAs<nonloc::PointerToMember>(),
+           RPTM = rhs.castAs<nonloc::PointerToMember>();
+      auto LPTMD = LPTM.getPTMData(), RPTMD = RPTM.getPTMData();
+      switch (op) {
+        case BO_EQ:
+          return makeTruthVal(LPTMD == RPTMD, resultTy);
+        case BO_NE:
+          return makeTruthVal(LPTMD != RPTMD, resultTy);
+        default:
+          return UnknownVal();
+      }
+    }
     case nonloc::LocAsIntegerKind: {
       Loc lhsL = lhs.castAs<nonloc::LocAsInteger>().getLoc();
       switch (rhs.getSubKind()) {
@@ -863,6 +881,23 @@ SVal SimpleSValBuilder::evalBinOpLL(Prog
 SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
                                   BinaryOperator::Opcode op,
                                   Loc lhs, NonLoc rhs, QualType resultTy) {
+  if (op >= BO_PtrMemD && op <= BO_PtrMemI) {
+    if (auto PTMSV = rhs.getAs<nonloc::PointerToMember>()) {
+      if (PTMSV->isNullMemberPointer())
+        return UndefinedVal();
+      if (const FieldDecl *FD = PTMSV->getDeclAs<FieldDecl>()) {
+        SVal Result = lhs;
+
+        for (const auto &I : *PTMSV)
+          Result = StateMgr.getStoreManager().evalDerivedToBase(
+              Result, I->getType(),I->isVirtual());
+        return state->getLValue(FD, Result);
+      }
+    }
+
+    return rhs;
+  }
+
   assert(!BinaryOperator::isComparisonOp(op) &&
          "arguments to comparison ops must be of the same type");
 

Modified: cfe/trunk/test/Analysis/pointer-to-member.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/pointer-to-member.cpp?rev=289873&r1=289872&r2=289873&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/pointer-to-member.cpp (original)
+++ cfe/trunk/test/Analysis/pointer-to-member.cpp Thu Dec 15 15:27:06 2016
@@ -35,8 +35,7 @@ void testComparison() {
   clang_analyzer_eval(&A::getPtr == &A::getPtr); // expected-warning{{TRUE}}
   clang_analyzer_eval(&A::getPtr == 0); // expected-warning{{FALSE}}
 
-  // FIXME: Should be TRUE.
-  clang_analyzer_eval(&A::m_ptr == &A::m_ptr); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(&A::m_ptr == &A::m_ptr); // expected-warning{{TRUE}}
 }
 
 namespace PR15742 {
@@ -62,21 +61,156 @@ namespace PR15742 {
   }
 }
 
-// ---------------
-// FALSE NEGATIVES
-// ---------------
-
 bool testDereferencing() {
   A obj;
   obj.m_ptr = 0;
 
   A::MemberPointer member = &A::m_ptr;
 
-  // FIXME: Should be TRUE.
-  clang_analyzer_eval(obj.*member == 0); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(obj.*member == 0); // expected-warning{{TRUE}}
 
   member = 0;
 
-  // FIXME: Should emit a null dereference.
-  return obj.*member; // no-warning
+  return obj.*member; // expected-warning{{The result of the '.*' expression is undefined}}
+}
+
+namespace testPointerToMemberFunction {
+  struct A {
+    virtual int foo() { return 1; }
+    int bar() { return 2;  }
+  };
+
+  struct B : public A {
+    virtual int foo() { return 3; }
+  };
+
+  typedef int (A::*AFnPointer)();
+  typedef int (B::*BFnPointer)();
+
+  void testPointerToMemberCasts() {
+    AFnPointer AFP = &A::bar;
+    BFnPointer StaticCastedBase2Derived = static_cast<BFnPointer>(&A::bar),
+               CCastedBase2Derived = (BFnPointer) (&A::bar);
+    A a;
+    B b;
+
+    clang_analyzer_eval((a.*AFP)() == 2); // expected-warning{{TRUE}}
+    clang_analyzer_eval((b.*StaticCastedBase2Derived)() == 2); // expected-warning{{TRUE}}
+    clang_analyzer_eval(((b.*CCastedBase2Derived)() == 2)); // expected-warning{{TRUE}}
+  }
+
+  void testPointerToMemberVirtualCall() {
+    A a;
+    B b;
+    A *APtr = &a;
+    AFnPointer AFP = &A::foo;
+
+    clang_analyzer_eval((APtr->*AFP)() == 1); // expected-warning{{TRUE}}
+
+    APtr = &b;
+
+    clang_analyzer_eval((APtr->*AFP)() == 3); // expected-warning{{TRUE}}
+  }
+} // end of testPointerToMemberFunction namespace
+
+namespace testPointerToMemberData {
+  struct A {
+    int i;
+  };
+
+  void testPointerToMemberData() {
+    int A::*AMdPointer = &A::i;
+    A a;
+
+    a.i = 42;
+    a.*AMdPointer += 1;
+
+    clang_analyzer_eval(a.i == 43); // expected-warning{{TRUE}}
+  }
+} // end of testPointerToMemberData namespace
+
+namespace testPointerToMemberMiscCasts {
+struct B {
+  int f;
+};
+
+struct D : public B {
+  int g;
+};
+
+void foo() {
+  D d;
+  d.f = 7;
+
+  int B::* pfb = &B::f;
+  int D::* pfd = pfb;
+  int v = d.*pfd;
+
+  clang_analyzer_eval(v == 7); // expected-warning{{TRUE}}
+}
+} // end of testPointerToMemberMiscCasts namespace
+
+namespace testPointerToMemberMiscCasts2 {
+struct B {
+  int f;
+};
+struct L : public B { };
+struct R : public B { };
+struct D : public L, R { };
+
+void foo() {
+  D d;
+
+  int B::* pb = &B::f;
+  int L::* pl = pb;
+  int R::* pr = pb;
+
+  int D::* pdl = pl;
+  int D::* pdr = pr;
+
+  clang_analyzer_eval(pdl == pdr); // expected-warning{{FALSE}}
+  clang_analyzer_eval(pb == pl); // expected-warning{{TRUE}}
+}
+} // end of testPointerToMemberMiscCasts2 namespace
+
+namespace testPointerToMemberDiamond {
+struct B {
+  int f;
+};
+struct L1 : public B { };
+struct R1 : public B { };
+struct M : public L1, R1 { };
+struct L2 : public M { };
+struct R2 : public M { };
+struct D2 : public L2, R2 { };
+
+void diamond() {
+  M m;
+
+  static_cast<L1 *>(&m)->f = 7;
+  static_cast<R1 *>(&m)->f = 16;
+
+  int L1::* pl1 = &B::f;
+  int M::* pm_via_l1 = pl1;
+
+  int R1::* pr1 = &B::f;
+  int M::* pm_via_r1 = pr1;
+
+  clang_analyzer_eval(m.*(pm_via_l1) == 7); // expected-warning {{TRUE}}
+  clang_analyzer_eval(m.*(pm_via_r1) == 16); // expected-warning {{TRUE}}
+}
+
+void double_diamond() {
+  D2 d2;
+
+  static_cast<L1 *>(static_cast<L2 *>(&d2))->f = 1;
+  static_cast<L1 *>(static_cast<R2 *>(&d2))->f = 2;
+  static_cast<R1 *>(static_cast<L2 *>(&d2))->f = 3;
+  static_cast<R1 *>(static_cast<R2 *>(&d2))->f = 4;
+
+  clang_analyzer_eval(d2.*(static_cast<int D2::*>(static_cast<int L2::*>(static_cast<int L1::*>(&B::f)))) == 1); // expected-warning {{TRUE}}
+  clang_analyzer_eval(d2.*(static_cast<int D2::*>(static_cast<int R2::*>(static_cast<int L1::*>(&B::f)))) == 2); // expected-warning {{TRUE}}
+  clang_analyzer_eval(d2.*(static_cast<int D2::*>(static_cast<int L2::*>(static_cast<int R1::*>(&B::f)))) == 3); // expected-warning {{TRUE}}
+  clang_analyzer_eval(d2.*(static_cast<int D2::*>(static_cast<int R2::*>(static_cast<int R1::*>(&B::f)))) == 4); // expected-warning {{TRUE}}
 }
+} // end of testPointerToMemberDiamond namespace




More information about the cfe-commits mailing list