This revision was automatically updated to reflect the committed changes. Closed by commit rGa364987368a1: [analyzer][NFC] Use `SValVisitor` instead of explicit helper functions (authored by ASDenysPetrov).
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D130029/new/ https://reviews.llvm.org/D130029 Files: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
Index: clang/lib/StaticAnalyzer/Core/SValBuilder.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ 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 @@ } //===----------------------------------------------------------------------===// -// 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 different 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 different 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 different 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 different 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 different 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 - // different 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 + // different 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 different 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 different 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 different 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); } Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -75,39 +75,6 @@ /// 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);
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits