[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