[clang] a364987 - [analyzer][NFC] Use `SValVisitor` instead of explicit helper functions

Denys Petrov via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 19 13:10:12 PDT 2022


Author: Denys Petrov
Date: 2022-07-19T23:10:00+03:00
New Revision: a364987368a1c586088c62eb7cffcdef0f4ee852

URL: https://github.com/llvm/llvm-project/commit/a364987368a1c586088c62eb7cffcdef0f4ee852
DIFF: https://github.com/llvm/llvm-project/commit/a364987368a1c586088c62eb7cffcdef0f4ee852.diff

LOG: [analyzer][NFC] Use `SValVisitor` instead of explicit helper functions

Summary: Get rid of explicit function splitting in favor of specifically designed Visitor. Move logic from a family of `evalCastKind` and `evalCastSubKind` helper functions to `SValVisitor`.

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

Added: 
    

Modified: 
    clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
    clang/lib/StaticAnalyzer/Core/SValBuilder.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
index 3d247e7887d70..1b9526324086d 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h
@@ -75,39 +75,6 @@ class SValBuilder {
   /// The width of the scalar type used for array indices.
   const unsigned ArrayIndexWidth;
 
-  SVal evalCastKind(UndefinedVal V, QualType CastTy, QualType OriginalTy);
-  SVal evalCastKind(UnknownVal V, QualType CastTy, QualType OriginalTy);
-  SVal evalCastKind(Loc V, QualType CastTy, QualType OriginalTy);
-  SVal evalCastKind(NonLoc V, QualType CastTy, QualType OriginalTy);
-  SVal evalCastSubKind(loc::ConcreteInt V, QualType CastTy,
-                       QualType OriginalTy);
-  SVal evalCastSubKind(loc::GotoLabel V, QualType CastTy, QualType OriginalTy);
-  SVal evalCastSubKind(loc::MemRegionVal V, QualType CastTy,
-                       QualType OriginalTy);
-  SVal evalCastSubKind(nonloc::CompoundVal V, QualType CastTy,
-                       QualType OriginalTy);
-  SVal evalCastSubKind(nonloc::ConcreteInt V, QualType CastTy,
-                       QualType OriginalTy);
-  SVal evalCastSubKind(nonloc::LazyCompoundVal V, QualType CastTy,
-                       QualType OriginalTy);
-  SVal evalCastSubKind(nonloc::LocAsInteger V, QualType CastTy,
-                       QualType OriginalTy);
-  SVal evalCastSubKind(nonloc::SymbolVal V, QualType CastTy,
-                       QualType OriginalTy);
-  SVal evalCastSubKind(nonloc::PointerToMember V, QualType CastTy,
-                       QualType OriginalTy);
-  /// Reduce cast expression by removing redundant intermediate casts.
-  /// E.g.
-  /// - (char)(short)(int x) -> (char)(int x)
-  /// - (int)(int x) -> int x
-  ///
-  /// \param V -- SymbolVal, which pressumably contains SymbolCast or any symbol
-  /// that is applicable for cast operation.
-  /// \param CastTy -- QualType, which `V` shall be cast to.
-  /// \return SVal with simplified cast expression.
-  /// \note: Currently only support integral casts.
-  nonloc::SymbolVal simplifySymbolCast(nonloc::SymbolVal V, QualType CastTy);
-
 public:
   SValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context,
               ProgramStateManager &stateMgr);

diff  --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index cf3d13ffb7ba7..d90e869196eb7 100644
--- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -19,15 +19,16 @@
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/Type.h"
-#include "clang/Basic/LLVM.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/Basic/LLVM.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
@@ -617,517 +618,478 @@ SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val,
 }
 
 //===----------------------------------------------------------------------===//
-// Cast methods.
-// `evalCast` is the main method
-// `evalCastKind` and `evalCastSubKind` are helpers
+// Cast method.
+// `evalCast` and its helper `EvalCastVisitor`
 //===----------------------------------------------------------------------===//
 
-/// Cast a given SVal to another SVal using given QualType's.
-/// \param V -- SVal that should be casted.
-/// \param CastTy -- QualType that V should be casted according to.
-/// \param OriginalTy -- QualType which is associated to V. It provides
-/// additional information about what type the cast performs from.
-/// \returns the most appropriate casted SVal.
-/// Note: Many cases don't use an exact OriginalTy. It can be extracted
-/// from SVal or the cast can performs unconditionaly. Always pass OriginalTy!
-/// It can be crucial in certain cases and generates 
diff erent results.
-/// FIXME: If `OriginalTy.isNull()` is true, then cast performs based on CastTy
-/// only. This behavior is uncertain and should be improved.
-SVal SValBuilder::evalCast(SVal V, QualType CastTy, QualType OriginalTy) {
-  if (CastTy.isNull())
-    return V;
-
-  CastTy = Context.getCanonicalType(CastTy);
+namespace {
+class EvalCastVisitor : public SValVisitor<EvalCastVisitor, SVal> {
+private:
+  SValBuilder &VB;
+  ASTContext &Context;
+  QualType CastTy, OriginalTy;
 
-  const bool IsUnknownOriginalType = OriginalTy.isNull();
-  if (!IsUnknownOriginalType) {
-    OriginalTy = Context.getCanonicalType(OriginalTy);
+public:
+  EvalCastVisitor(SValBuilder &VB, QualType CastTy, QualType OriginalTy)
+      : VB(VB), Context(VB.getContext()), CastTy(CastTy),
+        OriginalTy(OriginalTy) {}
 
-    if (CastTy == OriginalTy)
+  SVal Visit(SVal V) {
+    if (CastTy.isNull())
       return V;
 
-    // FIXME: Move this check to the most appropriate
-    // evalCastKind/evalCastSubKind function. For const casts, casts to void,
-    // just propagate the value.
-    if (!CastTy->isVariableArrayType() && !OriginalTy->isVariableArrayType())
-      if (shouldBeModeledWithNoOp(Context, Context.getPointerType(CastTy),
-                                  Context.getPointerType(OriginalTy)))
-        return V;
-  }
-
-  // Cast SVal according to kinds.
-  switch (V.getBaseKind()) {
-  case SVal::UndefinedValKind:
-    return evalCastKind(V.castAs<UndefinedVal>(), CastTy, OriginalTy);
-  case SVal::UnknownValKind:
-    return evalCastKind(V.castAs<UnknownVal>(), CastTy, OriginalTy);
-  case SVal::LocKind:
-    return evalCastKind(V.castAs<Loc>(), CastTy, OriginalTy);
-  case SVal::NonLocKind:
-    return evalCastKind(V.castAs<NonLoc>(), CastTy, OriginalTy);
-  }
-
-  llvm_unreachable("Unknown SVal kind");
-}
-
-SVal SValBuilder::evalCastKind(UndefinedVal V, QualType CastTy,
-                               QualType OriginalTy) {
-  return V;
-}
-
-SVal SValBuilder::evalCastKind(UnknownVal V, QualType CastTy,
-                               QualType OriginalTy) {
-  return V;
-}
-
-SVal SValBuilder::evalCastKind(Loc V, QualType CastTy, QualType OriginalTy) {
-  switch (V.getSubKind()) {
-  case loc::ConcreteIntKind:
-    return evalCastSubKind(V.castAs<loc::ConcreteInt>(), CastTy, OriginalTy);
-  case loc::GotoLabelKind:
-    return evalCastSubKind(V.castAs<loc::GotoLabel>(), CastTy, OriginalTy);
-  case loc::MemRegionValKind:
-    return evalCastSubKind(V.castAs<loc::MemRegionVal>(), CastTy, OriginalTy);
-  }
-
-  llvm_unreachable("Unknown SVal kind");
-}
-
-SVal SValBuilder::evalCastKind(NonLoc V, QualType CastTy, QualType OriginalTy) {
-  switch (V.getSubKind()) {
-  case nonloc::CompoundValKind:
-    return evalCastSubKind(V.castAs<nonloc::CompoundVal>(), CastTy, OriginalTy);
-  case nonloc::ConcreteIntKind:
-    return evalCastSubKind(V.castAs<nonloc::ConcreteInt>(), CastTy, OriginalTy);
-  case nonloc::LazyCompoundValKind:
-    return evalCastSubKind(V.castAs<nonloc::LazyCompoundVal>(), CastTy,
-                           OriginalTy);
-  case nonloc::LocAsIntegerKind:
-    return evalCastSubKind(V.castAs<nonloc::LocAsInteger>(), CastTy,
-                           OriginalTy);
-  case nonloc::SymbolValKind:
-    return evalCastSubKind(V.castAs<nonloc::SymbolVal>(), CastTy, OriginalTy);
-  case nonloc::PointerToMemberKind:
-    return evalCastSubKind(V.castAs<nonloc::PointerToMember>(), CastTy,
-                           OriginalTy);
-  }
+    CastTy = Context.getCanonicalType(CastTy);
 
-  llvm_unreachable("Unknown SVal kind");
-}
+    const bool IsUnknownOriginalType = OriginalTy.isNull();
+    if (!IsUnknownOriginalType) {
+      OriginalTy = Context.getCanonicalType(OriginalTy);
 
-SVal SValBuilder::evalCastSubKind(loc::ConcreteInt V, QualType CastTy,
-                                  QualType OriginalTy) {
-  // Pointer to bool.
-  if (CastTy->isBooleanType())
-    return makeTruthVal(V.getValue().getBoolValue(), CastTy);
-
-  // Pointer to integer.
-  if (CastTy->isIntegralOrEnumerationType()) {
-    llvm::APSInt Value = V.getValue();
-    BasicVals.getAPSIntType(CastTy).apply(Value);
-    return makeIntVal(Value);
-  }
+      if (CastTy == OriginalTy)
+        return V;
 
-  // Pointer to any pointer.
-  if (Loc::isLocType(CastTy)) {
-    llvm::APSInt Value = V.getValue();
-    BasicVals.getAPSIntType(CastTy).apply(Value);
-    return loc::ConcreteInt(BasicVals.getValue(Value));
+      // FIXME: Move this check to the most appropriate
+      // evalCastKind/evalCastSubKind function. For const casts, casts to void,
+      // just propagate the value.
+      if (!CastTy->isVariableArrayType() && !OriginalTy->isVariableArrayType())
+        if (shouldBeModeledWithNoOp(Context, Context.getPointerType(CastTy),
+                                    Context.getPointerType(OriginalTy)))
+          return V;
+    }
+    return SValVisitor::Visit(V);
   }
+  SVal VisitUndefinedVal(UndefinedVal V) { return V; }
+  SVal VisitUnknownVal(UnknownVal V) { return V; }
+  SVal VisitLocConcreteInt(loc::ConcreteInt V) {
+    // Pointer to bool.
+    if (CastTy->isBooleanType())
+      return VB.makeTruthVal(V.getValue().getBoolValue(), CastTy);
+
+    // Pointer to integer.
+    if (CastTy->isIntegralOrEnumerationType()) {
+      llvm::APSInt Value = V.getValue();
+      VB.getBasicValueFactory().getAPSIntType(CastTy).apply(Value);
+      return VB.makeIntVal(Value);
+    }
 
-  // Pointer to whatever else.
-  return UnknownVal();
-}
-
-SVal SValBuilder::evalCastSubKind(loc::GotoLabel V, QualType CastTy,
-                                  QualType OriginalTy) {
-  // Pointer to bool.
-  if (CastTy->isBooleanType())
-    // Labels are always true.
-    return makeTruthVal(true, CastTy);
-
-  // Pointer to integer.
-  if (CastTy->isIntegralOrEnumerationType()) {
-    const unsigned BitWidth = Context.getIntWidth(CastTy);
-    return makeLocAsInteger(V, BitWidth);
-  }
+    // Pointer to any pointer.
+    if (Loc::isLocType(CastTy)) {
+      llvm::APSInt Value = V.getValue();
+      VB.getBasicValueFactory().getAPSIntType(CastTy).apply(Value);
+      return loc::ConcreteInt(VB.getBasicValueFactory().getValue(Value));
+    }
 
-  const bool IsUnknownOriginalType = OriginalTy.isNull();
-  if (!IsUnknownOriginalType) {
-    // Array to pointer.
-    if (isa<ArrayType>(OriginalTy))
-      if (CastTy->isPointerType() || CastTy->isReferenceType())
-        return UnknownVal();
+    // Pointer to whatever else.
+    return UnknownVal();
   }
-
-  // Pointer to any pointer.
-  if (Loc::isLocType(CastTy))
-    return V;
-
-  // Pointer to whatever else.
-  return UnknownVal();
-}
-
-static bool hasSameUnqualifiedPointeeType(QualType ty1, QualType ty2) {
-  return ty1->getPointeeType().getCanonicalType().getTypePtr() ==
-         ty2->getPointeeType().getCanonicalType().getTypePtr();
-}
-
-SVal SValBuilder::evalCastSubKind(loc::MemRegionVal V, QualType CastTy,
-                                  QualType OriginalTy) {
-  // Pointer to bool.
-  if (CastTy->isBooleanType()) {
-    const MemRegion *R = V.getRegion();
-    if (const FunctionCodeRegion *FTR = dyn_cast<FunctionCodeRegion>(R))
-      if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(FTR->getDecl()))
-        if (FD->isWeak())
-          // FIXME: Currently we are using an extent symbol here,
-          // because there are no generic region address metadata
-          // symbols to use, only content metadata.
-          return nonloc::SymbolVal(SymMgr.getExtentSymbol(FTR));
-
-    if (const SymbolicRegion *SymR = R->getSymbolicBase()) {
-      SymbolRef Sym = SymR->getSymbol();
-      QualType Ty = Sym->getType();
-      // This change is needed for architectures with varying
-      // pointer widths. See the amdgcn opencl reproducer with
-      // this change as an example: solver-sym-simplification-ptr-bool.cl
-      if (!Ty->isReferenceType())
-        return makeNonLoc(Sym, BO_NE, BasicVals.getZeroWithTypeSize(Ty),
-                          CastTy);
+  SVal VisitLocGotoLabel(loc::GotoLabel V) {
+    // Pointer to bool.
+    if (CastTy->isBooleanType())
+      // Labels are always true.
+      return VB.makeTruthVal(true, CastTy);
+
+    // Pointer to integer.
+    if (CastTy->isIntegralOrEnumerationType()) {
+      const unsigned BitWidth = Context.getIntWidth(CastTy);
+      return VB.makeLocAsInteger(V, BitWidth);
     }
-    // Non-symbolic memory regions are always true.
-    return makeTruthVal(true, CastTy);
-  }
 
-  const bool IsUnknownOriginalType = OriginalTy.isNull();
-  // Try to cast to array
-  const auto *ArrayTy =
-      IsUnknownOriginalType
-          ? nullptr
-          : dyn_cast<ArrayType>(OriginalTy.getCanonicalType());
-
-  // Pointer to integer.
-  if (CastTy->isIntegralOrEnumerationType()) {
-    SVal Val = V;
-    // Array to integer.
-    if (ArrayTy) {
-      // We will always decay to a pointer.
-      QualType ElemTy = ArrayTy->getElementType();
-      Val = StateMgr.ArrayToPointer(V, ElemTy);
-      // FIXME: Keep these here for now in case we decide soon that we
-      // need the original decayed type.
-      //    QualType elemTy = cast<ArrayType>(originalTy)->getElementType();
-      //    QualType pointerTy = C.getPointerType(elemTy);
+    const bool IsUnknownOriginalType = OriginalTy.isNull();
+    if (!IsUnknownOriginalType) {
+      // Array to pointer.
+      if (isa<ArrayType>(OriginalTy))
+        if (CastTy->isPointerType() || CastTy->isReferenceType())
+          return UnknownVal();
     }
-    const unsigned BitWidth = Context.getIntWidth(CastTy);
-    return makeLocAsInteger(Val.castAs<Loc>(), BitWidth);
-  }
 
-  // Pointer to pointer.
-  if (Loc::isLocType(CastTy)) {
-
-    if (IsUnknownOriginalType) {
-      // When retrieving symbolic pointer and expecting a non-void pointer,
-      // wrap them into element regions of the expected type if necessary.
-      // It is necessary to make sure that the retrieved value makes sense,
-      // because there's no other cast in the AST that would tell us to cast
-      // it to the correct pointer type. We might need to do that for non-void
-      // pointers as well.
-      // FIXME: We really need a single good function to perform casts for us
-      // correctly every time we need it.
+    // Pointer to any pointer.
+    if (Loc::isLocType(CastTy))
+      return V;
+
+    // Pointer to whatever else.
+    return UnknownVal();
+  }
+  SVal VisitLocMemRegionVal(loc::MemRegionVal V) {
+    // Pointer to bool.
+    if (CastTy->isBooleanType()) {
       const MemRegion *R = V.getRegion();
-      if (CastTy->isPointerType() && !CastTy->isVoidPointerType()) {
-        if (const auto *SR = dyn_cast<SymbolicRegion>(R)) {
-          QualType SRTy = SR->getSymbol()->getType();
-          if (!hasSameUnqualifiedPointeeType(SRTy, CastTy)) {
-            if (auto OptMemRegV = getCastedMemRegionVal(SR, CastTy))
-              return *OptMemRegV;
-          }
-        }
-      }
-      // Next fixes pointer dereference using type 
diff erent from its initial
-      // one. See PR37503 and PR49007 for details.
-      if (const auto *ER = dyn_cast<ElementRegion>(R)) {
-        if (auto OptMemRegV = getCastedMemRegionVal(ER, CastTy))
-          return *OptMemRegV;
+      if (const FunctionCodeRegion *FTR = dyn_cast<FunctionCodeRegion>(R))
+        if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(FTR->getDecl()))
+          if (FD->isWeak())
+            // FIXME: Currently we are using an extent symbol here,
+            // because there are no generic region address metadata
+            // symbols to use, only content metadata.
+            return nonloc::SymbolVal(
+                VB.getSymbolManager().getExtentSymbol(FTR));
+
+      if (const SymbolicRegion *SymR = R->getSymbolicBase()) {
+        SymbolRef Sym = SymR->getSymbol();
+        QualType Ty = Sym->getType();
+        // This change is needed for architectures with varying
+        // pointer widths. See the amdgcn opencl reproducer with
+        // this change as an example: solver-sym-simplification-ptr-bool.cl
+        if (!Ty->isReferenceType())
+          return VB.makeNonLoc(
+              Sym, BO_NE, VB.getBasicValueFactory().getZeroWithTypeSize(Ty),
+              CastTy);
       }
-
-      return V;
+      // Non-symbolic memory regions are always true.
+      return VB.makeTruthVal(true, CastTy);
     }
 
-    if (OriginalTy->isIntegralOrEnumerationType() ||
-        OriginalTy->isBlockPointerType() || OriginalTy->isFunctionPointerType())
-      return V;
-
-    // Array to pointer.
-    if (ArrayTy) {
-      // Are we casting from an array to a pointer?  If so just pass on
-      // the decayed value.
-      if (CastTy->isPointerType() || CastTy->isReferenceType()) {
+    const bool IsUnknownOriginalType = OriginalTy.isNull();
+    // Try to cast to array
+    const auto *ArrayTy =
+        IsUnknownOriginalType
+            ? nullptr
+            : dyn_cast<ArrayType>(OriginalTy.getCanonicalType());
+
+    // Pointer to integer.
+    if (CastTy->isIntegralOrEnumerationType()) {
+      SVal Val = V;
+      // Array to integer.
+      if (ArrayTy) {
         // We will always decay to a pointer.
         QualType ElemTy = ArrayTy->getElementType();
-        return StateMgr.ArrayToPointer(V, ElemTy);
+        Val = VB.getStateManager().ArrayToPointer(V, ElemTy);
+        // FIXME: Keep these here for now in case we decide soon that we
+        // need the original decayed type.
+        //    QualType elemTy = cast<ArrayType>(originalTy)->getElementType();
+        //    QualType pointerTy = C.getPointerType(elemTy);
       }
-      // Are we casting from an array to an integer?  If so, cast the decayed
-      // pointer value to an integer.
-      assert(CastTy->isIntegralOrEnumerationType());
+      const unsigned BitWidth = Context.getIntWidth(CastTy);
+      return VB.makeLocAsInteger(Val.castAs<Loc>(), BitWidth);
     }
 
-    // Other pointer to pointer.
-    assert(Loc::isLocType(OriginalTy) || OriginalTy->isFunctionType() ||
-           CastTy->isReferenceType());
+    // Pointer to pointer.
+    if (Loc::isLocType(CastTy)) {
 
-    // We get a symbolic function pointer for a dereference of a function
-    // pointer, but it is of function type. Example:
+      if (IsUnknownOriginalType) {
+        // When retrieving symbolic pointer and expecting a non-void pointer,
+        // wrap them into element regions of the expected type if necessary.
+        // It is necessary to make sure that the retrieved value makes sense,
+        // because there's no other cast in the AST that would tell us to cast
+        // it to the correct pointer type. We might need to do that for non-void
+        // pointers as well.
+        // FIXME: We really need a single good function to perform casts for us
+        // correctly every time we need it.
+        const MemRegion *R = V.getRegion();
+        if (CastTy->isPointerType() && !CastTy->isVoidPointerType()) {
+          if (const auto *SR = dyn_cast<SymbolicRegion>(R)) {
+            QualType SRTy = SR->getSymbol()->getType();
+
+            auto HasSameUnqualifiedPointeeType = [](QualType ty1,
+                                                    QualType ty2) {
+              return ty1->getPointeeType().getCanonicalType().getTypePtr() ==
+                     ty2->getPointeeType().getCanonicalType().getTypePtr();
+            };
+            if (!HasSameUnqualifiedPointeeType(SRTy, CastTy)) {
+              if (auto OptMemRegV = VB.getCastedMemRegionVal(SR, CastTy))
+                return *OptMemRegV;
+            }
+          }
+        }
+        // Next fixes pointer dereference using type 
diff erent from its initial
+        // one. See PR37503 and PR49007 for details.
+        if (const auto *ER = dyn_cast<ElementRegion>(R)) {
+          if (auto OptMemRegV = VB.getCastedMemRegionVal(ER, CastTy))
+            return *OptMemRegV;
+        }
 
-    //  struct FPRec {
-    //    void (*my_func)(int * x);
-    //  };
-    //
-    //  int bar(int x);
-    //
-    //  int f1_a(struct FPRec* foo) {
-    //    int x;
-    //    (*foo->my_func)(&x);
-    //    return bar(x)+1; // no-warning
-    //  }
-
-    // Get the result of casting a region to a 
diff erent type.
-    const MemRegion *R = V.getRegion();
-    if (auto OptMemRegV = getCastedMemRegionVal(R, CastTy))
-      return *OptMemRegV;
-  }
+        return V;
+      }
 
-  // Pointer to whatever else.
-  // FIXME: There can be gross cases where one casts the result of a
-  // function (that returns a pointer) to some other value that happens to
-  // fit within that pointer value.  We currently have no good way to model
-  // such operations.  When this happens, the underlying operation is that
-  // the caller is reasoning about bits.  Conceptually we are layering a
-  // "view" of a location on top of those bits.  Perhaps we need to be more
-  // lazy about mutual possible views, even on an SVal?  This may be
-  // necessary for bit-level reasoning as well.
-  return UnknownVal();
-}
+      if (OriginalTy->isIntegralOrEnumerationType() ||
+          OriginalTy->isBlockPointerType() ||
+          OriginalTy->isFunctionPointerType())
+        return V;
 
-SVal SValBuilder::evalCastSubKind(nonloc::CompoundVal V, QualType CastTy,
-                                  QualType OriginalTy) {
-  // Compound to whatever.
-  return UnknownVal();
-}
+      // Array to pointer.
+      if (ArrayTy) {
+        // Are we casting from an array to a pointer?  If so just pass on
+        // the decayed value.
+        if (CastTy->isPointerType() || CastTy->isReferenceType()) {
+          // We will always decay to a pointer.
+          QualType ElemTy = ArrayTy->getElementType();
+          return VB.getStateManager().ArrayToPointer(V, ElemTy);
+        }
+        // Are we casting from an array to an integer?  If so, cast the decayed
+        // pointer value to an integer.
+        assert(CastTy->isIntegralOrEnumerationType());
+      }
 
-SVal SValBuilder::evalCastSubKind(nonloc::ConcreteInt V, QualType CastTy,
-                                  QualType OriginalTy) {
-  auto CastedValue = [V, CastTy, this]() {
-    llvm::APSInt Value = V.getValue();
-    BasicVals.getAPSIntType(CastTy).apply(Value);
-    return Value;
-  };
+      // Other pointer to pointer.
+      assert(Loc::isLocType(OriginalTy) || OriginalTy->isFunctionType() ||
+             CastTy->isReferenceType());
 
-  // Integer to bool.
-  if (CastTy->isBooleanType())
-    return makeTruthVal(V.getValue().getBoolValue(), CastTy);
+      // We get a symbolic function pointer for a dereference of a function
+      // pointer, but it is of function type. Example:
+
+      //  struct FPRec {
+      //    void (*my_func)(int * x);
+      //  };
+      //
+      //  int bar(int x);
+      //
+      //  int f1_a(struct FPRec* foo) {
+      //    int x;
+      //    (*foo->my_func)(&x);
+      //    return bar(x)+1; // no-warning
+      //  }
+
+      // Get the result of casting a region to a 
diff erent type.
+      const MemRegion *R = V.getRegion();
+      if (auto OptMemRegV = VB.getCastedMemRegionVal(R, CastTy))
+        return *OptMemRegV;
+    }
 
-  // Integer to pointer.
-  if (CastTy->isIntegralOrEnumerationType())
-    return makeIntVal(CastedValue());
+    // Pointer to whatever else.
+    // FIXME: There can be gross cases where one casts the result of a
+    // function (that returns a pointer) to some other value that happens to
+    // fit within that pointer value.  We currently have no good way to model
+    // such operations.  When this happens, the underlying operation is that
+    // the caller is reasoning about bits.  Conceptually we are layering a
+    // "view" of a location on top of those bits.  Perhaps we need to be more
+    // lazy about mutual possible views, even on an SVal?  This may be
+    // necessary for bit-level reasoning as well.
+    return UnknownVal();
+  }
+  SVal VisitNonLocCompoundVal(nonloc::CompoundVal V) {
+    // Compound to whatever.
+    return UnknownVal();
+  }
+  SVal VisitNonLocConcreteInt(nonloc::ConcreteInt V) {
+    auto CastedValue = [V, this]() {
+      llvm::APSInt Value = V.getValue();
+      VB.getBasicValueFactory().getAPSIntType(CastTy).apply(Value);
+      return Value;
+    };
 
-  // Integer to pointer.
-  if (Loc::isLocType(CastTy))
-    return makeIntLocVal(CastedValue());
+    // Integer to bool.
+    if (CastTy->isBooleanType())
+      return VB.makeTruthVal(V.getValue().getBoolValue(), CastTy);
 
-  // Pointer to whatever else.
-  return UnknownVal();
-}
+    // Integer to pointer.
+    if (CastTy->isIntegralOrEnumerationType())
+      return VB.makeIntVal(CastedValue());
 
-SVal SValBuilder::evalCastSubKind(nonloc::LazyCompoundVal V, QualType CastTy,
-                                  QualType OriginalTy) {
-  // Compound to whatever.
-  return UnknownVal();
-}
+    // Integer to pointer.
+    if (Loc::isLocType(CastTy))
+      return VB.makeIntLocVal(CastedValue());
 
-SVal SValBuilder::evalCastSubKind(nonloc::LocAsInteger V, QualType CastTy,
-                                  QualType OriginalTy) {
-  Loc L = V.getLoc();
-
-  // Pointer as integer to bool.
-  if (CastTy->isBooleanType())
-    // Pass to Loc function.
-    return evalCastKind(L, CastTy, OriginalTy);
-
-  const bool IsUnknownOriginalType = OriginalTy.isNull();
-  // Pointer as integer to pointer.
-  if (!IsUnknownOriginalType && Loc::isLocType(CastTy) &&
-      OriginalTy->isIntegralOrEnumerationType()) {
-    if (const MemRegion *R = L.getAsRegion())
-      if (auto OptMemRegV = getCastedMemRegionVal(R, CastTy))
-        return *OptMemRegV;
-    return L;
+    // Pointer to whatever else.
+    return UnknownVal();
   }
-
-  // Pointer as integer with region to integer/pointer.
-  const MemRegion *R = L.getAsRegion();
-  if (!IsUnknownOriginalType && R) {
-    if (CastTy->isIntegralOrEnumerationType())
-      return evalCastSubKind(loc::MemRegionVal(R), CastTy, OriginalTy);
-
-    if (Loc::isLocType(CastTy)) {
-      assert(Loc::isLocType(OriginalTy) || OriginalTy->isFunctionType() ||
-             CastTy->isReferenceType());
-      // Delegate to store manager to get the result of casting a region to a
-      // 
diff erent type. If the MemRegion* returned is NULL, this expression
-      // Evaluates to UnknownVal.
-      if (auto OptMemRegV = getCastedMemRegionVal(R, CastTy))
-        return *OptMemRegV;
-    }
-  } else {
-    if (Loc::isLocType(CastTy)) {
-      if (IsUnknownOriginalType)
-        return evalCastSubKind(loc::MemRegionVal(R), CastTy, OriginalTy);
+  SVal VisitNonLocLazyCompoundVal(nonloc::LazyCompoundVal V) {
+    // LazyCompound to whatever.
+    return UnknownVal();
+  }
+  SVal VisitNonLocLocAsInteger(nonloc::LocAsInteger V) {
+    Loc L = V.getLoc();
+
+    // Pointer as integer to bool.
+    if (CastTy->isBooleanType())
+      // Pass to Loc function.
+      return Visit(L);
+
+    const bool IsUnknownOriginalType = OriginalTy.isNull();
+    // Pointer as integer to pointer.
+    if (!IsUnknownOriginalType && Loc::isLocType(CastTy) &&
+        OriginalTy->isIntegralOrEnumerationType()) {
+      if (const MemRegion *R = L.getAsRegion())
+        if (auto OptMemRegV = VB.getCastedMemRegionVal(R, CastTy))
+          return *OptMemRegV;
       return L;
     }
 
-    SymbolRef SE = nullptr;
-    if (R) {
-      if (const SymbolicRegion *SR =
-              dyn_cast<SymbolicRegion>(R->StripCasts())) {
-        SE = SR->getSymbol();
+    // Pointer as integer with region to integer/pointer.
+    const MemRegion *R = L.getAsRegion();
+    if (!IsUnknownOriginalType && R) {
+      if (CastTy->isIntegralOrEnumerationType())
+        return VisitLocMemRegionVal(loc::MemRegionVal(R));
+
+      if (Loc::isLocType(CastTy)) {
+        assert(Loc::isLocType(OriginalTy) || OriginalTy->isFunctionType() ||
+               CastTy->isReferenceType());
+        // Delegate to store manager to get the result of casting a region to a
+        // 
diff erent type. If the MemRegion* returned is NULL, this expression
+        // Evaluates to UnknownVal.
+        if (auto OptMemRegV = VB.getCastedMemRegionVal(R, CastTy))
+          return *OptMemRegV;
+      }
+    } else {
+      if (Loc::isLocType(CastTy)) {
+        if (IsUnknownOriginalType)
+          return VisitLocMemRegionVal(loc::MemRegionVal(R));
+        return L;
       }
-    }
-
-    if (!CastTy->isFloatingType() || !SE || SE->getType()->isFloatingType()) {
-      // FIXME: Correctly support promotions/truncations.
-      const unsigned CastSize = Context.getIntWidth(CastTy);
-      if (CastSize == V.getNumBits())
-        return V;
 
-      return makeLocAsInteger(L, CastSize);
-    }
-  }
+      SymbolRef SE = nullptr;
+      if (R) {
+        if (const SymbolicRegion *SR =
+                dyn_cast<SymbolicRegion>(R->StripCasts())) {
+          SE = SR->getSymbol();
+        }
+      }
 
-  // Pointer as integer to whatever else.
-  return UnknownVal();
-}
+      if (!CastTy->isFloatingType() || !SE || SE->getType()->isFloatingType()) {
+        // FIXME: Correctly support promotions/truncations.
+        const unsigned CastSize = Context.getIntWidth(CastTy);
+        if (CastSize == V.getNumBits())
+          return V;
 
-SVal SValBuilder::evalCastSubKind(nonloc::SymbolVal V, QualType CastTy,
-                                  QualType OriginalTy) {
-  SymbolRef SE = V.getSymbol();
-
-  const bool IsUnknownOriginalType = OriginalTy.isNull();
-  // Symbol to bool.
-  if (!IsUnknownOriginalType && CastTy->isBooleanType()) {
-    // Non-float to bool.
-    if (Loc::isLocType(OriginalTy) ||
-        OriginalTy->isIntegralOrEnumerationType() ||
-        OriginalTy->isMemberPointerType()) {
-      BasicValueFactory &BVF = getBasicValueFactory();
-      return makeNonLoc(SE, BO_NE, BVF.getValue(0, SE->getType()), CastTy);
+        return VB.makeLocAsInteger(L, CastSize);
+      }
     }
-  } else {
-    // Symbol to integer, float.
-    QualType T = Context.getCanonicalType(SE->getType());
 
-    // Produce SymbolCast if CastTy and T are 
diff erent integers.
-    // NOTE: In the end the type of SymbolCast shall be equal to CastTy.
-    if (T->isIntegralOrUnscopedEnumerationType() &&
-        CastTy->isIntegralOrUnscopedEnumerationType()) {
-      AnalyzerOptions &Opts =
-          StateMgr.getOwningEngine().getAnalysisManager().getAnalyzerOptions();
-      // If appropriate option is disabled, ignore the cast.
-      // NOTE: ShouldSupportSymbolicIntegerCasts is `false` by default.
-      if (!Opts.ShouldSupportSymbolicIntegerCasts)
-        return V;
-      return simplifySymbolCast(V, CastTy);
+    // Pointer as integer to whatever else.
+    return UnknownVal();
+  }
+  SVal VisitNonLocSymbolVal(nonloc::SymbolVal V) {
+    SymbolRef SE = V.getSymbol();
+
+    const bool IsUnknownOriginalType = OriginalTy.isNull();
+    // Symbol to bool.
+    if (!IsUnknownOriginalType && CastTy->isBooleanType()) {
+      // Non-float to bool.
+      if (Loc::isLocType(OriginalTy) ||
+          OriginalTy->isIntegralOrEnumerationType() ||
+          OriginalTy->isMemberPointerType()) {
+        BasicValueFactory &BVF = VB.getBasicValueFactory();
+        return VB.makeNonLoc(SE, BO_NE, BVF.getValue(0, SE->getType()), CastTy);
+      }
+    } else {
+      // Symbol to integer, float.
+      QualType T = Context.getCanonicalType(SE->getType());
+
+      // Produce SymbolCast if CastTy and T are 
diff erent integers.
+      // NOTE: In the end the type of SymbolCast shall be equal to CastTy.
+      if (T->isIntegralOrUnscopedEnumerationType() &&
+          CastTy->isIntegralOrUnscopedEnumerationType()) {
+        AnalyzerOptions &Opts = VB.getStateManager()
+                                    .getOwningEngine()
+                                    .getAnalysisManager()
+                                    .getAnalyzerOptions();
+        // If appropriate option is disabled, ignore the cast.
+        // NOTE: ShouldSupportSymbolicIntegerCasts is `false` by default.
+        if (!Opts.ShouldSupportSymbolicIntegerCasts)
+          return V;
+        return simplifySymbolCast(V, CastTy);
+      }
+      if (!Loc::isLocType(CastTy))
+        if (!IsUnknownOriginalType || !CastTy->isFloatingType() ||
+            T->isFloatingType())
+          return VB.makeNonLoc(SE, T, CastTy);
     }
-    if (!Loc::isLocType(CastTy))
-      if (!IsUnknownOriginalType || !CastTy->isFloatingType() ||
-          T->isFloatingType())
-        return makeNonLoc(SE, T, CastTy);
+
+    // Symbol to pointer and whatever else.
+    return UnknownVal();
+  }
+  SVal VisitNonLocPointerToMember(nonloc::PointerToMember V) {
+    // Member pointer to whatever.
+    return V;
   }
 
-  // Symbol to pointer and whatever else.
-  return UnknownVal();
-}
+  /// Reduce cast expression by removing redundant intermediate casts.
+  /// E.g.
+  /// - (char)(short)(int x) -> (char)(int x)
+  /// - (int)(int x) -> int x
+  ///
+  /// \param V -- SymbolVal, which pressumably contains SymbolCast or any symbol
+  /// that is applicable for cast operation.
+  /// \param CastTy -- QualType, which `V` shall be cast to.
+  /// \return SVal with simplified cast expression.
+  /// \note: Currently only support integral casts.
+  nonloc::SymbolVal simplifySymbolCast(nonloc::SymbolVal V, QualType CastTy) {
+    // We use seven conditions to recognize a simplification case.
+    // For the clarity let `CastTy` be `C`, SE->getType() - `T`, root type -
+    // `R`, prefix `u` for unsigned, `s` for signed, no prefix - any sign: E.g.
+    // (char)(short)(uint x)
+    //      ( sC )( sT  )( uR  x)
+    //
+    // C === R (the same type)
+    //  (char)(char x) -> (char x)
+    //  (long)(long x) -> (long x)
+    // Note: Comparisons operators below are for bit width.
+    // C == T
+    //  (short)(short)(int x) -> (short)(int x)
+    //  (int)(long)(char x) -> (int)(char x) (sizeof(long) == sizeof(int))
+    //  (long)(ullong)(char x) -> (long)(char x) (sizeof(long) ==
+    //  sizeof(ullong))
+    // C < T
+    //  (short)(int)(char x) -> (short)(char x)
+    //  (char)(int)(short x) -> (char)(short x)
+    //  (short)(int)(short x) -> (short x)
+    // C > T > uR
+    //  (int)(short)(uchar x) -> (int)(uchar x)
+    //  (uint)(short)(uchar x) -> (uint)(uchar x)
+    //  (int)(ushort)(uchar x) -> (int)(uchar x)
+    // C > sT > sR
+    //  (int)(short)(char x) -> (int)(char x)
+    //  (uint)(short)(char x) -> (uint)(char x)
+    // C > sT == sR
+    //  (int)(char)(char x) -> (int)(char x)
+    //  (uint)(short)(short x) -> (uint)(short x)
+    // C > uT == uR
+    //  (int)(uchar)(uchar x) -> (int)(uchar x)
+    //  (uint)(ushort)(ushort x) -> (uint)(ushort x)
+    //  (llong)(ulong)(uint x) -> (llong)(uint x) (sizeof(ulong) ==
+    //  sizeof(uint))
+
+    SymbolRef SE = V.getSymbol();
+    QualType T = Context.getCanonicalType(SE->getType());
 
-SVal SValBuilder::evalCastSubKind(nonloc::PointerToMember V, QualType CastTy,
-                                  QualType OriginalTy) {
-  // Member pointer to whatever.
-  return V;
-}
+    if (T == CastTy)
+      return V;
 
-nonloc::SymbolVal SValBuilder::simplifySymbolCast(nonloc::SymbolVal V,
-                                                  QualType CastTy) {
-  // We use seven conditions to recognize a simplification case.
-  // For the clarity let `CastTy` be `C`, SE->getType() - `T`, root type - `R`,
-  // prefix `u` for unsigned, `s` for signed, no prefix - any sign:
-  // E.g. (char)(short)(uint x)
-  //      ( sC )( sT  )( uR  x)
-  //
-  // C === R (the same type)
-  //  (char)(char x) -> (char x)
-  //  (long)(long x) -> (long x)
-  // Note: Comparisons operators below are for bit width.
-  // C == T
-  //  (short)(short)(int x) -> (short)(int x)
-  //  (int)(long)(char x) -> (int)(char x) (sizeof(long) == sizeof(int))
-  //  (long)(ullong)(char x) -> (long)(char x) (sizeof(long) == sizeof(ullong))
-  // C < T
-  //  (short)(int)(char x) -> (short)(char x)
-  //  (char)(int)(short x) -> (char)(short x)
-  //  (short)(int)(short x) -> (short x)
-  // C > T > uR
-  //  (int)(short)(uchar x) -> (int)(uchar x)
-  //  (uint)(short)(uchar x) -> (uint)(uchar x)
-  //  (int)(ushort)(uchar x) -> (int)(uchar x)
-  // C > sT > sR
-  //  (int)(short)(char x) -> (int)(char x)
-  //  (uint)(short)(char x) -> (uint)(char x)
-  // C > sT == sR
-  //  (int)(char)(char x) -> (int)(char x)
-  //  (uint)(short)(short x) -> (uint)(short x)
-  // C > uT == uR
-  //  (int)(uchar)(uchar x) -> (int)(uchar x)
-  //  (uint)(ushort)(ushort x) -> (uint)(ushort x)
-  //  (llong)(ulong)(uint x) -> (llong)(uint x) (sizeof(ulong) == sizeof(uint))
-
-  SymbolRef SE = V.getSymbol();
-  QualType T = Context.getCanonicalType(SE->getType());
-
-  if (T == CastTy)
-    return V;
+    if (!isa<SymbolCast>(SE))
+      return VB.makeNonLoc(SE, T, CastTy);
 
-  if (!isa<SymbolCast>(SE))
-    return makeNonLoc(SE, T, CastTy);
+    SymbolRef RootSym = cast<SymbolCast>(SE)->getOperand();
+    QualType RT = RootSym->getType().getCanonicalType();
 
-  SymbolRef RootSym = cast<SymbolCast>(SE)->getOperand();
-  QualType RT = RootSym->getType().getCanonicalType();
+    // FIXME support simplification from non-integers.
+    if (!RT->isIntegralOrEnumerationType())
+      return VB.makeNonLoc(SE, T, CastTy);
 
-  // FIXME support simplification from non-integers.
-  if (!RT->isIntegralOrEnumerationType())
-    return makeNonLoc(SE, T, CastTy);
+    BasicValueFactory &BVF = VB.getBasicValueFactory();
+    APSIntType CTy = BVF.getAPSIntType(CastTy);
+    APSIntType TTy = BVF.getAPSIntType(T);
 
-  BasicValueFactory &BVF = getBasicValueFactory();
-  APSIntType CTy = BVF.getAPSIntType(CastTy);
-  APSIntType TTy = BVF.getAPSIntType(T);
+    const auto WC = CTy.getBitWidth();
+    const auto WT = TTy.getBitWidth();
 
-  const auto WC = CTy.getBitWidth();
-  const auto WT = TTy.getBitWidth();
+    if (WC <= WT) {
+      const bool isSameType = (RT == CastTy);
+      if (isSameType)
+        return nonloc::SymbolVal(RootSym);
+      return VB.makeNonLoc(RootSym, RT, CastTy);
+    }
 
-  if (WC <= WT) {
-    const bool isSameType = (RT == CastTy);
-    if (isSameType)
-      return nonloc::SymbolVal(RootSym);
-    return makeNonLoc(RootSym, RT, CastTy);
-  }
+    APSIntType RTy = BVF.getAPSIntType(RT);
+    const auto WR = RTy.getBitWidth();
+    const bool UT = TTy.isUnsigned();
+    const bool UR = RTy.isUnsigned();
 
-  APSIntType RTy = BVF.getAPSIntType(RT);
-  const auto WR = RTy.getBitWidth();
-  const bool UT = TTy.isUnsigned();
-  const bool UR = RTy.isUnsigned();
+    if (((WT > WR) && (UR || !UT)) || ((WT == WR) && (UT == UR)))
+      return VB.makeNonLoc(RootSym, RT, CastTy);
 
-  if (((WT > WR) && (UR || !UT)) || ((WT == WR) && (UT == UR)))
-    return makeNonLoc(RootSym, RT, CastTy);
+    return VB.makeNonLoc(SE, T, CastTy);
+  }
+};
+} // end anonymous namespace
 
-  return makeNonLoc(SE, T, CastTy);
+/// Cast a given SVal to another SVal using given QualType's.
+/// \param V -- SVal that should be casted.
+/// \param CastTy -- QualType that V should be casted according to.
+/// \param OriginalTy -- QualType which is associated to V. It provides
+/// additional information about what type the cast performs from.
+/// \returns the most appropriate casted SVal.
+/// Note: Many cases don't use an exact OriginalTy. It can be extracted
+/// from SVal or the cast can performs unconditionaly. Always pass OriginalTy!
+/// It can be crucial in certain cases and generates 
diff erent results.
+/// FIXME: If `OriginalTy.isNull()` is true, then cast performs based on CastTy
+/// only. This behavior is uncertain and should be improved.
+SVal SValBuilder::evalCast(SVal V, QualType CastTy, QualType OriginalTy) {
+  EvalCastVisitor TRV{*this, CastTy, OriginalTy};
+  return TRV.Visit(V);
 }


        


More information about the cfe-commits mailing list