Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -152,6 +152,10 @@ HelpText<"Loss of sign/precision in implicit conversions">, DescFile<"ConversionChecker.cpp">; +def FloatingPointMathChecker : Checker<"FPMath">, + HelpText<"Check for domain errors in floating-point math functions">, + DescFile<"FloatingPointMath.cpp">; + def IdenticalExprChecker : Checker<"IdenticalExpr">, HelpText<"Warn about unintended use of identical expressions in operators">, DescFile<"IdenticalExprChecker.cpp">; Index: include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h @@ -18,9 +18,9 @@ #include "clang/AST/ASTContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" namespace clang { namespace ento { @@ -38,15 +38,16 @@ iterator begin() const { return L.begin(); } iterator end() const { return L.end(); } - static void Profile(llvm::FoldingSetNodeID& ID, QualType T, + static void Profile(llvm::FoldingSetNodeID &ID, QualType T, llvm::ImmutableList L); - void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, T, L); } + void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, T, L); } }; class LazyCompoundValData : public llvm::FoldingSetNode { StoreRef store; const TypedValueRegion *region; + public: LazyCompoundValData(const StoreRef &st, const TypedValueRegion *r) : store(st), region(r) { @@ -56,70 +57,75 @@ const void *getStore() const { return store.getStore(); } const TypedValueRegion *getRegion() const { return region; } - static void Profile(llvm::FoldingSetNodeID& ID, - const StoreRef &store, + static void Profile(llvm::FoldingSetNodeID &ID, const StoreRef &store, const TypedValueRegion *region); - void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, store, region); } + void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, store, region); } }; -class PointerToMemberData: public llvm::FoldingSetNode { +class PointerToMemberData : public llvm::FoldingSetNode { const DeclaratorDecl *D; llvm::ImmutableList L; public: PointerToMemberData(const DeclaratorDecl *D, llvm::ImmutableList L) - : D(D), L(L) {} + : D(D), L(L) {} typedef llvm::ImmutableList::iterator iterator; iterator begin() const { return L.begin(); } iterator end() const { return L.end(); } - static void Profile(llvm::FoldingSetNodeID& ID, const DeclaratorDecl *D, + static void Profile(llvm::FoldingSetNodeID &ID, const DeclaratorDecl *D, llvm::ImmutableList L); - void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, D, L); } - const DeclaratorDecl *getDeclaratorDecl() const {return D;} + void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, D, L); } + const DeclaratorDecl *getDeclaratorDecl() const { return D; } llvm::ImmutableList getCXXBaseList() const { return L; } }; class BasicValueFactory { - typedef llvm::FoldingSet > - APSIntSetTy; + typedef llvm::FoldingSet> + APSIntSetTy; + typedef llvm::FoldingSet> + APFloatSetTy; ASTContext &Ctx; - llvm::BumpPtrAllocator& BPAlloc; + llvm::BumpPtrAllocator &BPAlloc; - APSIntSetTy APSIntSet; - void * PersistentSVals; - void * PersistentSValPairs; + APSIntSetTy APSIntSet; + APFloatSetTy APFloatSet; + void *PersistentSVals; + void *PersistentSValPairs; llvm::ImmutableList::Factory SValListFactory; - llvm::ImmutableList::Factory CXXBaseListFactory; - llvm::FoldingSet CompoundValDataSet; + llvm::ImmutableList::Factory CXXBaseListFactory; + llvm::FoldingSet CompoundValDataSet; llvm::FoldingSet LazyCompoundValDataSet; llvm::FoldingSet PointerToMemberDataSet; // This is private because external clients should use the factory // method that takes a QualType. - const llvm::APSInt& getValue(uint64_t X, unsigned BitWidth, bool isUnsigned); + const llvm::APSInt &getValue(uint64_t X, unsigned BitWidth, bool isUnsigned); public: BasicValueFactory(ASTContext &ctx, llvm::BumpPtrAllocator &Alloc) - : Ctx(ctx), BPAlloc(Alloc), PersistentSVals(nullptr), - PersistentSValPairs(nullptr), SValListFactory(Alloc), - CXXBaseListFactory(Alloc) {} + : Ctx(ctx), BPAlloc(Alloc), PersistentSVals(nullptr), + PersistentSValPairs(nullptr), SValListFactory(Alloc), + CXXBaseListFactory(Alloc) {} ~BasicValueFactory(); ASTContext &getContext() const { return Ctx; } - const llvm::APSInt& getValue(const llvm::APSInt& X); - const llvm::APSInt& getValue(const llvm::APInt& X, bool isUnsigned); - const llvm::APSInt& getValue(uint64_t X, QualType T); + const llvm::APSInt &getValue(const llvm::APSInt &X); + const llvm::APSInt &getValue(const llvm::APInt &X, bool isUnsigned); + const llvm::APSInt &getValue(uint64_t X, QualType T); + + const llvm::APFloat &getValue(const llvm::APFloat &X); + const llvm::APFloat &getValue(int64_t X, const llvm::fltSemantics &S); /// Returns the type of the APSInt used to store values of the given QualType. APSIntType getAPSIntType(QualType T) const { @@ -128,64 +134,106 @@ !T->isSignedIntegerOrEnumerationType()); } + // Returns whether the floating-point conversion completed successfully + static bool checkFloatConversion(const llvm::APFloat::opStatus Status) { + return !(Status & (llvm::APFloat::opOverflow | llvm::APFloat::opInvalidOp)); + } + /// Convert - Create a new persistent APSInt with the same value as 'From' /// but with the bitwidth and signedness of 'To'. - const llvm::APSInt &Convert(const llvm::APSInt& To, - const llvm::APSInt& From) { + const llvm::APSInt &Convert(const llvm::APSInt &To, + const llvm::APSInt &From) { APSIntType TargetType(To); if (TargetType == APSIntType(From)) return From; return getValue(TargetType.convert(From)); } - + const llvm::APSInt &Convert(QualType T, const llvm::APSInt &From) { APSIntType TargetType = getAPSIntType(T); if (TargetType == APSIntType(From)) return From; - + return getValue(TargetType.convert(From)); } - const llvm::APSInt& getIntValue(uint64_t X, bool isUnsigned) { + const llvm::APFloat &Convert(const llvm::APSInt &From, + const llvm::fltSemantics &S) { + llvm::APFloat To(S, llvm::APFloat::uninitialized); + bool result = Convert(To, From); + assert(result && "Failed to convert integer to floating-point!"); + (void)result; + return getValue(To); + } + + const llvm::APFloat &Convert(QualType T, const llvm::APFloat &From) { + bool lossOfPrecision; + llvm::APFloat To = From; + bool isOk = checkFloatConversion( + To.convert(Ctx.getFloatTypeSemantics(T), + llvm::APFloat::rmNearestTiesToEven, &lossOfPrecision)); + assert(isOk && "Failed to convert integer to floating-point!"); + (void)isOk; + return getValue(To); + } + + /// Fills an existing APFloat with the floating-point representation of an + /// APSInt. Returns true if the conversion is successful. + static bool Convert(llvm::APFloat &Float, const llvm::APSInt &Int) { + // Cannot be represented in destination type, this is undefined behavior + return checkFloatConversion(Float.convertFromAPInt( + Int, Int.isSigned(), llvm::APFloat::rmNearestTiesToEven)); + } + + /// Fills an existing APSInt with the integer representation of an + /// APFloat. Returns true if the conversion is successful. + static bool Convert(llvm::APSInt &Int, const llvm::APFloat &Float) { + bool isExact; + // Cannot be represented in destination type, this is undefined behavior + return checkFloatConversion(Float.convertToInteger( + Int, llvm::APFloat::rmNearestTiesToEven, &isExact)); + } + + const llvm::APSInt &getIntValue(uint64_t X, bool isUnsigned) { QualType T = isUnsigned ? Ctx.UnsignedIntTy : Ctx.IntTy; return getValue(X, T); } - inline const llvm::APSInt& getMaxValue(const llvm::APSInt &v) { + inline const llvm::APSInt &getMaxValue(const llvm::APSInt &v) { return getValue(APSIntType(v).getMaxValue()); } - inline const llvm::APSInt& getMinValue(const llvm::APSInt &v) { + inline const llvm::APSInt &getMinValue(const llvm::APSInt &v) { return getValue(APSIntType(v).getMinValue()); } - inline const llvm::APSInt& getMaxValue(QualType T) { + inline const llvm::APSInt &getMaxValue(QualType T) { return getValue(getAPSIntType(T).getMaxValue()); } - inline const llvm::APSInt& getMinValue(QualType T) { + inline const llvm::APSInt &getMinValue(QualType T) { return getValue(getAPSIntType(T).getMinValue()); } - inline const llvm::APSInt& Add1(const llvm::APSInt& V) { + inline const llvm::APSInt &Add1(const llvm::APSInt &V) { llvm::APSInt X = V; ++X; return getValue(X); } - inline const llvm::APSInt& Sub1(const llvm::APSInt& V) { + inline const llvm::APSInt &Sub1(const llvm::APSInt &V) { llvm::APSInt X = V; --X; return getValue(X); } - inline const llvm::APSInt& getZeroWithTypeSize(QualType T) { + inline const llvm::APSInt &getZeroWithTypeSize(QualType T) { assert(T->isScalarType()); return getValue(0, Ctx.getTypeSize(T), true); } - inline const llvm::APSInt& getZeroWithPtrWidth(bool isUnsigned = true) { + inline const llvm::APSInt &getZeroWithPtrWidth(bool isUnsigned = true) { return getValue(0, Ctx.getTypeSize(Ctx.VoidPtrTy), isUnsigned); } @@ -193,23 +241,23 @@ return getValue(X, Ctx.getTypeSize(Ctx.VoidPtrTy), isUnsigned); } - inline const llvm::APSInt& getTruthValue(bool b, QualType T) { + inline const llvm::APSInt &getTruthValue(bool b, QualType T) { return getValue(b ? 1 : 0, Ctx.getTypeSize(T), false); } - inline const llvm::APSInt& getTruthValue(bool b) { + inline const llvm::APSInt &getTruthValue(bool b) { return getTruthValue(b, Ctx.getLogicalOperationType()); } const CompoundValData *getCompoundValData(QualType T, llvm::ImmutableList Vals); - const LazyCompoundValData *getLazyCompoundValData(const StoreRef &store, - const TypedValueRegion *region); + const LazyCompoundValData * + getLazyCompoundValData(const StoreRef &store, const TypedValueRegion *region); - const PointerToMemberData *getPointerToMemberData( - const DeclaratorDecl *DD, - llvm::ImmutableList L); + const PointerToMemberData * + getPointerToMemberData(const DeclaratorDecl *DD, + llvm::ImmutableList L); llvm::ImmutableList getEmptySValList() { return SValListFactory.getEmptyList(); @@ -223,31 +271,39 @@ return CXXBaseListFactory.getEmptyList(); } - llvm::ImmutableList prependCXXBase( - const CXXBaseSpecifier *CBS, - llvm::ImmutableList L) { + llvm::ImmutableList + prependCXXBase(const CXXBaseSpecifier *CBS, + llvm::ImmutableList L) { return CXXBaseListFactory.add(CBS, L); } - const clang::ento::PointerToMemberData *accumCXXBase( - llvm::iterator_range PathRange, - const nonloc::PointerToMember &PTM); + const clang::ento::PointerToMemberData * + accumCXXBase(llvm::iterator_range PathRange, + const nonloc::PointerToMember &PTM); + + const llvm::APSInt *evalAPSInt(BinaryOperator::Opcode Op, + const llvm::APSInt &V1, + const llvm::APSInt &V2); + + const llvm::APFloat *evalAPFloat(BinaryOperator::Opcode Op, + const llvm::APFloat &V1, + const llvm::APFloat &V2); - const llvm::APSInt* evalAPSInt(BinaryOperator::Opcode Op, - const llvm::APSInt& V1, - const llvm::APSInt& V2); + const llvm::APSInt *evalAPFloatComparison(BinaryOperator::Opcode Op, + const llvm::APFloat &V1, + const llvm::APFloat &V2); - const std::pair& - getPersistentSValWithData(const SVal& V, uintptr_t Data); + const std::pair &getPersistentSValWithData(const SVal &V, + uintptr_t Data); - const std::pair& - getPersistentSValPair(const SVal& V1, const SVal& V2); + const std::pair &getPersistentSValPair(const SVal &V1, + const SVal &V2); - const SVal* getPersistentSVal(SVal X); + const SVal *getPersistentSVal(SVal X); }; -} // end GR namespace +} // namespace ento -} // end clang namespace +} // namespace clang #endif Index: include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h @@ -29,6 +29,7 @@ class ConditionTruthVal { Optional Val; + public: /// Construct a ConditionTruthVal indicating the constraint is constrained /// to either true or false, depending on the boolean value provided. @@ -38,25 +39,17 @@ ConditionTruthVal() {} /// Return true if the constraint is perfectly constrained to 'true'. - bool isConstrainedTrue() const { - return Val.hasValue() && Val.getValue(); - } + bool isConstrainedTrue() const { return Val.hasValue() && Val.getValue(); } /// Return true if the constraint is perfectly constrained to 'false'. - bool isConstrainedFalse() const { - return Val.hasValue() && !Val.getValue(); - } + bool isConstrainedFalse() const { return Val.hasValue() && !Val.getValue(); } /// Return true if the constrained is perfectly constrained. - bool isConstrained() const { - return Val.hasValue(); - } + bool isConstrained() const { return Val.hasValue(); } /// Return true if the constrained is underconstrained and we do not know /// if the constraint is true of value. - bool isUnderconstrained() const { - return !Val.hasValue(); - } + bool isUnderconstrained() const { return !Val.hasValue(); } }; class ConstraintManager { @@ -64,8 +57,7 @@ ConstraintManager() : NotifyAssumeClients(true) {} virtual ~ConstraintManager(); - virtual ProgramStateRef assume(ProgramStateRef state, - DefinedSVal Cond, + virtual ProgramStateRef assume(ProgramStateRef state, DefinedSVal Cond, bool Assumption) = 0; typedef std::pair ProgramStatePair; @@ -93,7 +85,7 @@ // We are careful to return the original state, /not/ StTrue, // because we want to avoid having callers generate a new node // in the ExplodedGraph. - return ProgramStatePair(State, (ProgramStateRef)nullptr); + return ProgramStatePair(State, (ProgramStateRef) nullptr); } return ProgramStatePair(StTrue, StFalse); @@ -115,7 +107,7 @@ // If StTrue is infeasible, asserting the falseness of Cond is unnecessary // because the existing constraints already establish this. if (!StInRange) - return ProgramStatePair((ProgramStateRef)nullptr, State); + return ProgramStatePair((ProgramStateRef) nullptr, State); ProgramStateRef StOutOfRange = assumeInclusiveRange(State, Value, From, To, false); @@ -123,7 +115,7 @@ // We are careful to return the original state, /not/ StTrue, // because we want to avoid having callers generate a new node // in the ExplodedGraph. - return ProgramStatePair(State, (ProgramStateRef)nullptr); + return ProgramStatePair(State, (ProgramStateRef) nullptr); } return ProgramStatePair(StInRange, StOutOfRange); @@ -134,19 +126,27 @@ /// /// Note that a ConstraintManager is not obligated to return a concretized /// value for a symbol, even if it is perfectly constrained. - virtual const llvm::APSInt* getSymVal(ProgramStateRef state, - SymbolRef sym) const { + virtual const llvm::APSInt *getSymIntVal(ProgramStateRef state, + SymbolRef sym) const { + return nullptr; + } + + /// \brief If a symbol is perfectly constrained to a constant, attempt + /// to return the concrete value. + /// + /// Note that a ConstraintManager is not obligated to return a concretized + /// value for a symbol, even if it is perfectly constrained. + virtual const llvm::APFloat *getSymFloatVal(ProgramStateRef state, + SymbolRef sym) const { return nullptr; } /// Scan all symbols referenced by the constraints. If the symbol is not /// alive, remove it. virtual ProgramStateRef removeDeadBindings(ProgramStateRef state, - SymbolReaper& SymReaper) = 0; + SymbolReaper &SymReaper) = 0; - virtual void print(ProgramStateRef state, - raw_ostream &Out, - const char* nl, + virtual void print(ProgramStateRef state, raw_ostream &Out, const char *nl, const char *sep) = 0; virtual void EndPath(ProgramStateRef state) {} @@ -159,6 +159,11 @@ return checkNull(State, Sym); } + /// canReasonAbout - Not all ConstraintManagers can reason about floating- + /// point values. Instead of first instantiating a floating-point SVal to + /// query whether it is supported, use this to ask directly. + virtual bool canReasonAboutFloats() const = 0; + protected: /// A flag to indicate that clients should be notified of assumptions. /// By default this is the case, but sometimes this needs to be restricted @@ -187,8 +192,8 @@ std::unique_ptr CreateZ3ConstraintManager(ProgramStateManager &statemgr, SubEngine *subengine); -} // end GR namespace +} // namespace ento -} // end clang namespace +} // namespace clang #endif Index: include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -31,9 +31,10 @@ class SValBuilder { virtual void anchor(); + protected: ASTContext &Context; - + /// Manager of APSInt values. BasicValueFactory BasicVals; @@ -47,7 +48,7 @@ /// The scalar type to use for array indices. const QualType ArrayIndexTy; - + /// The width of the scalar type used for array indices. const unsigned ArrayIndexWidth; @@ -62,12 +63,10 @@ public: SValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context, ProgramStateManager &stateMgr) - : Context(context), BasicVals(context, alloc), - SymMgr(context, BasicVals, alloc), - MemMgr(context, alloc), - StateMgr(stateMgr), - ArrayIndexTy(context.LongLongTy), - ArrayIndexWidth(context.getTypeSize(ArrayIndexTy)) {} + : Context(context), BasicVals(context, alloc), + SymMgr(context, BasicVals, alloc), MemMgr(context, alloc), + StateMgr(stateMgr), ArrayIndexTy(context.LongLongTy), + ArrayIndexWidth(context.getTypeSize(ArrayIndexTy)) {} virtual ~SValBuilder() {} @@ -109,22 +108,28 @@ virtual SVal evalBinOpLN(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, NonLoc rhs, QualType resultTy) = 0; - /// Evaluates a given SVal. If the SVal has only one possible (integer) value, + /// Evaluates a given SVal. If the SVal has only one possible integer value, /// that value is returned. Otherwise, returns NULL. - virtual const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal val) = 0; + virtual const llvm::APSInt *getKnownIntValue(ProgramStateRef state, + SVal val) = 0; + + /// Evaluates a given SVal. If the SVal has only one possible float value, + /// that value is returned. Otherwise, returns NULL. + virtual const llvm::APFloat *getKnownFloatValue(ProgramStateRef state, + SVal val) = 0; /// Simplify symbolic expressions within a given SVal. Return an SVal /// that represents the same value, but is hopefully easier to work with /// than the original SVal. virtual SVal simplifySVal(ProgramStateRef State, SVal Val) = 0; - + /// Constructs a symbolic expression for two non-location values. SVal makeSymExprValNN(ProgramStateRef state, BinaryOperator::Opcode op, - NonLoc lhs, NonLoc rhs, QualType resultTy); + NonLoc lhs, NonLoc rhs, QualType resultTy); + + SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, + SVal rhs, QualType type); - SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, - SVal lhs, SVal rhs, QualType type); - DefinedOrUnknownSVal evalEQ(ProgramStateRef state, DefinedOrUnknownSVal lhs, DefinedOrUnknownSVal rhs); @@ -132,14 +137,12 @@ const ASTContext &getContext() const { return Context; } ProgramStateManager &getStateManager() { return StateMgr; } - + QualType getConditionType() const { return Context.getLangOpts().CPlusPlus ? Context.BoolTy : Context.IntTy; } - - QualType getArrayIndexType() const { - return ArrayIndexTy; - } + + QualType getArrayIndexType() const { return ArrayIndexTy; } BasicValueFactory &getBasicValueFactory() { return BasicVals; } const BasicValueFactory &getBasicValueFactory() const { return BasicVals; } @@ -152,15 +155,14 @@ // Forwarding methods to SymbolManager. - const SymbolConjured* conjureSymbol(const Stmt *stmt, + const SymbolConjured *conjureSymbol(const Stmt *stmt, const LocationContext *LCtx, - QualType type, - unsigned visitCount, + QualType type, unsigned visitCount, const void *symbolTag = nullptr) { return SymMgr.conjureSymbol(stmt, LCtx, type, visitCount, symbolTag); } - const SymbolConjured* conjureSymbol(const Expr *expr, + const SymbolConjured *conjureSymbol(const Expr *expr, const LocationContext *LCtx, unsigned visitCount, const void *symbolTag = nullptr) { @@ -179,20 +181,16 @@ /// The advantage of symbols derived/built from other symbols is that we /// preserve the relation between related(or even equivalent) expressions, so /// conjured symbols should be used sparingly. - DefinedOrUnknownSVal conjureSymbolVal(const void *symbolTag, - const Expr *expr, + DefinedOrUnknownSVal conjureSymbolVal(const void *symbolTag, const Expr *expr, const LocationContext *LCtx, unsigned count); - DefinedOrUnknownSVal conjureSymbolVal(const void *symbolTag, - const Expr *expr, + DefinedOrUnknownSVal conjureSymbolVal(const void *symbolTag, const Expr *expr, const LocationContext *LCtx, - QualType type, - unsigned count); - + QualType type, unsigned count); + DefinedOrUnknownSVal conjureSymbolVal(const Stmt *stmt, const LocationContext *LCtx, - QualType type, - unsigned visitCount); + QualType type, unsigned visitCount); /// \brief Conjure a symbol representing heap allocated memory region. /// /// Note, the expression should represent a location. @@ -200,19 +198,19 @@ const LocationContext *LCtx, unsigned Count); - DefinedOrUnknownSVal getDerivedRegionValueSymbolVal( - SymbolRef parentSymbol, const TypedValueRegion *region); + DefinedOrUnknownSVal + getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, + const TypedValueRegion *region); DefinedSVal getMetadataSymbolVal(const void *symbolTag, - const MemRegion *region, - const Expr *expr, QualType type, - const LocationContext *LCtx, + const MemRegion *region, const Expr *expr, + QualType type, const LocationContext *LCtx, unsigned count); DefinedSVal getMemberPointer(const DeclaratorDecl *DD); DefinedSVal getFunctionPointer(const FunctionDecl *func); - + DefinedSVal getBlockPointer(const BlockDecl *block, CanQualType locTy, const LocationContext *locContext, unsigned blockCount); @@ -227,7 +225,7 @@ return nonloc::CompoundVal(BasicVals.getCompoundValData(type, vals)); } - NonLoc makeLazyCompoundVal(const StoreRef &store, + NonLoc makeLazyCompoundVal(const StoreRef &store, const TypedValueRegion *region) { return nonloc::LazyCompoundVal( BasicVals.getLazyCompoundValData(store, region)); @@ -251,10 +249,10 @@ SVal convertToArrayIndex(SVal val); - nonloc::ConcreteInt makeIntVal(const IntegerLiteral* integer) { - return nonloc::ConcreteInt( - BasicVals.getValue(integer->getValue(), - integer->getType()->isUnsignedIntegerOrEnumerationType())); + nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer) { + return nonloc::ConcreteInt(BasicVals.getValue( + integer->getValue(), + integer->getType()->isUnsignedIntegerOrEnumerationType())); } nonloc::ConcreteInt makeBoolVal(const ObjCBoolLiteralExpr *boolean) { @@ -263,7 +261,7 @@ nonloc::ConcreteInt makeBoolVal(const CXXBoolLiteralExpr *boolean); - nonloc::ConcreteInt makeIntVal(const llvm::APSInt& integer) { + nonloc::ConcreteInt makeIntVal(const llvm::APSInt &integer) { return nonloc::ConcreteInt(BasicVals.getValue(integer)); } @@ -271,7 +269,7 @@ return loc::ConcreteInt(BasicVals.getValue(integer)); } - NonLoc makeIntVal(const llvm::APInt& integer, bool isUnsigned) { + NonLoc makeIntVal(const llvm::APInt &integer, bool isUnsigned) { return nonloc::ConcreteInt(BasicVals.getValue(integer, isUnsigned)); } @@ -295,13 +293,33 @@ return nonloc::LocAsInteger(BasicVals.getPersistentSValWithData(loc, bits)); } + nonloc::ConcreteFloat makeFloatVal(const FloatingLiteral *F) { + return nonloc::ConcreteFloat(BasicVals.getValue(F->getValue())); + } + + nonloc::ConcreteFloat makeFloatVal(const llvm::APFloat &F) { + return nonloc::ConcreteFloat(BasicVals.getValue(F)); + } + + DefinedSVal makeFloatVal(uint64_t V, QualType T) { + assert(!Loc::isLocType(T)); + return nonloc::ConcreteFloat( + BasicVals.getValue(V, Context.getFloatTypeSemantics(T))); + } + NonLoc makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, - const llvm::APSInt& rhs, QualType type); + const llvm::APSInt &rhs, QualType type); - NonLoc makeNonLoc(const llvm::APSInt& rhs, BinaryOperator::Opcode op, + NonLoc makeNonLoc(const llvm::APSInt &rhs, BinaryOperator::Opcode op, const SymExpr *lhs, QualType type); NonLoc makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, + const llvm::APFloat &rhs, QualType type); + + NonLoc makeNonLoc(const llvm::APFloat &lhs, BinaryOperator::Opcode op, + const SymExpr *rhs, QualType type); + + NonLoc makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType type); /// \brief Create a NonLoc value for cast. @@ -322,23 +340,19 @@ return loc::ConcreteInt(BasicVals.getZeroWithTypeSize(type)); } - Loc makeNull() { - return loc::ConcreteInt(BasicVals.getZeroWithPtrWidth()); - } + Loc makeNull() { return loc::ConcreteInt(BasicVals.getZeroWithPtrWidth()); } Loc makeLoc(SymbolRef sym) { return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); } - Loc makeLoc(const MemRegion* region) { - return loc::MemRegionVal(region); - } + Loc makeLoc(const MemRegion *region) { return loc::MemRegionVal(region); } Loc makeLoc(const AddrLabelExpr *expr) { return loc::GotoLabel(expr->getLabel()); } - Loc makeLoc(const llvm::APSInt& integer) { + Loc makeLoc(const llvm::APSInt &integer) { return loc::ConcreteInt(BasicVals.getValue(integer)); } @@ -351,12 +365,12 @@ const StackFrameContext *SFC); }; -SValBuilder* createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc, +SValBuilder *createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context, ProgramStateManager &stateMgr); -} // end GR namespace +} // namespace ento -} // end clang namespace +} // namespace clang #endif Index: include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -141,10 +141,14 @@ return getRawKind() > UnknownValKind; } + bool isFloat() const; + bool isConstant() const; bool isConstant(int I) const; + bool isConstant(llvm::APFloat &V) const; + bool isZeroConstant() const; /// hasConjuredSymbol - If this SVal wraps a conjured symbol, return true; @@ -193,7 +197,7 @@ return SymExpr::symbol_iterator(); } - SymExpr::symbol_iterator symbol_end() const { + SymExpr::symbol_iterator symbol_end() const { return SymExpr::symbol_end(); } }; @@ -216,26 +220,26 @@ // tautologically false. bool isUndef() const = delete; bool isValid() const = delete; - + protected: DefinedOrUnknownSVal() {} explicit DefinedOrUnknownSVal(const void *d, bool isLoc, unsigned ValKind) : SVal(d, isLoc, ValKind) {} - + explicit DefinedOrUnknownSVal(BaseKind k, void *D = nullptr) : SVal(k, D) {} - + private: friend class SVal; static bool isKind(const SVal& V) { return !V.isUndef(); } }; - + class UnknownVal : public DefinedOrUnknownSVal { public: explicit UnknownVal() : DefinedOrUnknownSVal(UnknownValKind) {} - + private: friend class SVal; static bool isKind(const SVal &V) { @@ -305,7 +309,7 @@ void dumpToStream(raw_ostream &Out) const; static inline bool isLocType(QualType T) { - return T->isAnyPointerType() || T->isBlockPointerType() || + return T->isAnyPointerType() || T->isBlockPointerType() || T->isReferenceType() || T->isNullPtrType(); } @@ -378,6 +382,34 @@ } }; +/// \brief Value representing floating-point constant. +class ConcreteFloat : public NonLoc { +public: + explicit ConcreteFloat(const llvm::APFloat& V) : NonLoc(ConcreteFloatKind, &V) {} + + const llvm::APFloat& getValue() const { + return *static_cast(Data); + } + + // Transfer functions for binary/unary operations on ConcreteFloats. + SVal evalBinOp(SValBuilder &svalBuilder, BinaryOperator::Opcode Op, + const ConcreteFloat& R) const; + + ConcreteFloat evalMinus(SValBuilder &svalBuilder) const; + +private: + friend class SVal; + ConcreteFloat() {} + static bool isKind(const SVal& V) { + return V.getBaseKind() == NonLocKind && + V.getSubKind() == ConcreteFloatKind; + } + + static bool isKind(const NonLoc& V) { + return V.getSubKind() == ConcreteFloatKind; + } +}; + class LocAsInteger : public NonLoc { friend class ento::SValBuilder; Index: include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def +++ include/clang/StaticAnalyzer/Core/PathSensitive/SVals.def @@ -63,6 +63,7 @@ ABSTRACT_SVAL_WITH_KIND(NonLoc, DefinedSVal) NONLOC_SVAL(CompoundVal, NonLoc) NONLOC_SVAL(ConcreteInt, NonLoc) + NONLOC_SVAL(ConcreteFloat, NonLoc) NONLOC_SVAL(LazyCompoundVal, NonLoc) NONLOC_SVAL(LocAsInteger, NonLoc) NONLOC_SVAL(SymbolVal, NonLoc) Index: include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -29,14 +29,14 @@ #include "llvm/Support/DataTypes.h" namespace clang { - class ASTContext; - class StackFrameContext; +class ASTContext; +class StackFrameContext; namespace ento { - class BasicValueFactory; - class SubRegion; - class TypedValueRegion; - class VarRegion; +class BasicValueFactory; +class SubRegion; +class TypedValueRegion; +class VarRegion; ///\brief A symbol representing the value stored at a MemRegion. class SymbolRegionValue : public SymbolData { @@ -49,14 +49,15 @@ assert(isValidTypeForSymbol(r->getValueType())); } - const TypedValueRegion* getRegion() const { return R; } + const TypedValueRegion *getRegion() const { return R; } - static void Profile(llvm::FoldingSetNodeID& profile, const TypedValueRegion* R) { - profile.AddInteger((unsigned) SymbolRegionValueKind); + static void Profile(llvm::FoldingSetNodeID &profile, + const TypedValueRegion *R) { + profile.AddInteger((unsigned)SymbolRegionValueKind); profile.AddPointer(R); } - void Profile(llvm::FoldingSetNodeID& profile) override { + void Profile(llvm::FoldingSetNodeID &profile) override { Profile(profile, R); } @@ -102,10 +103,10 @@ void dumpToStream(raw_ostream &os) const override; - static void Profile(llvm::FoldingSetNodeID& profile, const Stmt *S, + static void Profile(llvm::FoldingSetNodeID &profile, const Stmt *S, QualType T, unsigned Count, const LocationContext *LCtx, const void *SymbolTag) { - profile.AddInteger((unsigned) SymbolConjuredKind); + profile.AddInteger((unsigned)SymbolConjuredKind); profile.AddPointer(S); profile.AddPointer(LCtx); profile.Add(T); @@ -113,7 +114,7 @@ profile.AddPointer(SymbolTag); } - void Profile(llvm::FoldingSetNodeID& profile) override { + void Profile(llvm::FoldingSetNodeID &profile) override { Profile(profile, S, T, Count, LCtx, SymbolTag); } @@ -145,14 +146,14 @@ void dumpToStream(raw_ostream &os) const override; const MemRegion *getOriginRegion() const override { return getRegion(); } - static void Profile(llvm::FoldingSetNodeID& profile, SymbolRef parent, + static void Profile(llvm::FoldingSetNodeID &profile, SymbolRef parent, const TypedValueRegion *r) { - profile.AddInteger((unsigned) SymbolDerivedKind); + profile.AddInteger((unsigned)SymbolDerivedKind); profile.AddPointer(r); profile.AddPointer(parent); } - void Profile(llvm::FoldingSetNodeID& profile) override { + void Profile(llvm::FoldingSetNodeID &profile) override { Profile(profile, parentSymbol, R); } @@ -167,7 +168,7 @@ /// SubRegion::getExtent instead -- the value returned may not be a symbol. class SymbolExtent : public SymbolData { const SubRegion *R; - + public: SymbolExtent(SymbolID sym, const SubRegion *r) : SymbolData(SymbolExtentKind, sym), R(r) { @@ -180,12 +181,12 @@ void dumpToStream(raw_ostream &os) const override; - static void Profile(llvm::FoldingSetNodeID& profile, const SubRegion *R) { - profile.AddInteger((unsigned) SymbolExtentKind); + static void Profile(llvm::FoldingSetNodeID &profile, const SubRegion *R) { + profile.AddInteger((unsigned)SymbolExtentKind); profile.AddPointer(R); } - void Profile(llvm::FoldingSetNodeID& profile) override { + void Profile(llvm::FoldingSetNodeID &profile) override { Profile(profile, R); } @@ -200,23 +201,24 @@ /// dead-symbol sweeping AND their associated regions are still alive. /// Intended for use by checkers. class SymbolMetadata : public SymbolData { - const MemRegion* R; + const MemRegion *R; const Stmt *S; QualType T; const LocationContext *LCtx; unsigned Count; const void *Tag; + public: - SymbolMetadata(SymbolID sym, const MemRegion* r, const Stmt *s, QualType t, + SymbolMetadata(SymbolID sym, const MemRegion *r, const Stmt *s, QualType t, const LocationContext *LCtx, unsigned count, const void *tag) - : SymbolData(SymbolMetadataKind, sym), R(r), S(s), T(t), LCtx(LCtx), - Count(count), Tag(tag) { - assert(r); - assert(s); - assert(isValidTypeForSymbol(t)); - assert(LCtx); - assert(tag); - } + : SymbolData(SymbolMetadataKind, sym), R(r), S(s), T(t), LCtx(LCtx), + Count(count), Tag(tag) { + assert(r); + assert(s); + assert(isValidTypeForSymbol(t)); + assert(LCtx); + assert(tag); + } const MemRegion *getRegion() const { return R; } const Stmt *getStmt() const { return S; } @@ -228,10 +230,10 @@ void dumpToStream(raw_ostream &os) const override; - static void Profile(llvm::FoldingSetNodeID& profile, const MemRegion *R, + static void Profile(llvm::FoldingSetNodeID &profile, const MemRegion *R, const Stmt *S, QualType T, const LocationContext *LCtx, unsigned Count, const void *Tag) { - profile.AddInteger((unsigned) SymbolMetadataKind); + profile.AddInteger((unsigned)SymbolMetadataKind); profile.AddPointer(R); profile.AddPointer(S); profile.Add(T); @@ -240,7 +242,7 @@ profile.AddPointer(Tag); } - void Profile(llvm::FoldingSetNodeID& profile) override { + void Profile(llvm::FoldingSetNodeID &profile) override { Profile(profile, R, S, T, LCtx, Count, Tag); } @@ -273,15 +275,15 @@ void dumpToStream(raw_ostream &os) const override; - static void Profile(llvm::FoldingSetNodeID& ID, - const SymExpr *In, QualType From, QualType To) { - ID.AddInteger((unsigned) SymbolCastKind); + static void Profile(llvm::FoldingSetNodeID &ID, const SymExpr *In, + QualType From, QualType To) { + ID.AddInteger((unsigned)SymbolCastKind); ID.AddPointer(In); ID.Add(From); ID.Add(To); } - void Profile(llvm::FoldingSetNodeID& ID) override { + void Profile(llvm::FoldingSetNodeID &ID) override { Profile(ID, Operand, FromTy, ToTy); } @@ -291,7 +293,7 @@ } }; -/// \brief Represents a symbolic expression involving a binary operator +/// \brief Represents a symbolic expression involving a binary operator class BinarySymExpr : public SymExpr { BinaryOperator::Opcode Op; QualType T; @@ -320,7 +322,7 @@ /// \brief Represents a symbolic expression like 'x' + 3. class SymIntExpr : public BinarySymExpr { const SymExpr *LHS; - const llvm::APSInt& RHS; + const llvm::APSInt &RHS; public: SymIntExpr(const SymExpr *lhs, BinaryOperator::Opcode op, @@ -334,17 +336,17 @@ const SymExpr *getLHS() const { return LHS; } const llvm::APSInt &getRHS() const { return RHS; } - static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *lhs, - BinaryOperator::Opcode op, const llvm::APSInt& rhs, + static void Profile(llvm::FoldingSetNodeID &ID, const SymExpr *lhs, + BinaryOperator::Opcode op, const llvm::APSInt &rhs, QualType t) { - ID.AddInteger((unsigned) SymIntExprKind); + ID.AddInteger((unsigned)SymIntExprKind); ID.AddPointer(lhs); ID.AddInteger(op); ID.AddPointer(&rhs); ID.Add(t); } - void Profile(llvm::FoldingSetNodeID& ID) override { + void Profile(llvm::FoldingSetNodeID &ID) override { Profile(ID, LHS, getOpcode(), RHS, getType()); } @@ -356,7 +358,7 @@ /// \brief Represents a symbolic expression like 3 - 'x'. class IntSymExpr : public BinarySymExpr { - const llvm::APSInt& LHS; + const llvm::APSInt &LHS; const SymExpr *RHS; public: @@ -371,17 +373,17 @@ const SymExpr *getRHS() const { return RHS; } const llvm::APSInt &getLHS() const { return LHS; } - static void Profile(llvm::FoldingSetNodeID& ID, const llvm::APSInt& lhs, + static void Profile(llvm::FoldingSetNodeID &ID, const llvm::APSInt &lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType t) { - ID.AddInteger((unsigned) IntSymExprKind); + ID.AddInteger((unsigned)IntSymExprKind); ID.AddPointer(&lhs); ID.AddInteger(op); ID.AddPointer(rhs); ID.Add(t); } - void Profile(llvm::FoldingSetNodeID& ID) override { + void Profile(llvm::FoldingSetNodeID &ID) override { Profile(ID, LHS, getOpcode(), RHS, getType()); } @@ -391,6 +393,76 @@ } }; +/// \brief Represents a symbolic expression like 'x' + 3.0. +class SymFloatExpr : public BinarySymExpr { + const SymExpr *LHS; + const llvm::APFloat &RHS; + +public: + SymFloatExpr(const SymExpr *lhs, BinaryOperator::Opcode op, + const llvm::APFloat &rhs, QualType t) + : BinarySymExpr(SymFloatExprKind, op, t), LHS(lhs), RHS(rhs) {} + + void dumpToStream(raw_ostream &os) const override; + + const SymExpr *getLHS() const { return LHS; } + const llvm::APFloat &getRHS() const { return RHS; } + + static void Profile(llvm::FoldingSetNodeID &ID, const SymExpr *lhs, + BinaryOperator::Opcode op, const llvm::APFloat &rhs, + QualType t) { + ID.AddInteger((unsigned)SymFloatExprKind); + ID.AddPointer(lhs); + ID.AddInteger(op); + ID.AddPointer(&rhs); + ID.Add(t); + } + + void Profile(llvm::FoldingSetNodeID &ID) override { + Profile(ID, LHS, getOpcode(), RHS, getType()); + } + + // Implement isa support. + static inline bool classof(const SymExpr *SE) { + return SE->getKind() == SymFloatExprKind; + } +}; + +/// \brief Represents a symbolic expression like 3.0 - 'x'. +class FloatSymExpr : public BinarySymExpr { + const llvm::APFloat &LHS; + const SymExpr *RHS; + +public: + FloatSymExpr(const llvm::APFloat &lhs, BinaryOperator::Opcode op, + const SymExpr *rhs, QualType t) + : BinarySymExpr(FloatSymExprKind, op, t), LHS(lhs), RHS(rhs) {} + + void dumpToStream(raw_ostream &os) const override; + + const SymExpr *getRHS() const { return RHS; } + const llvm::APFloat &getLHS() const { return LHS; } + + static void Profile(llvm::FoldingSetNodeID &ID, const llvm::APFloat &lhs, + BinaryOperator::Opcode op, const SymExpr *rhs, + QualType t) { + ID.AddInteger((unsigned)FloatSymExprKind); + ID.AddPointer(&lhs); + ID.AddInteger(op); + ID.AddPointer(rhs); + ID.Add(t); + } + + void Profile(llvm::FoldingSetNodeID &ID) override { + Profile(ID, LHS, getOpcode(), RHS, getType()); + } + + // Implement isa support. + static inline bool classof(const SymExpr *SE) { + return SE->getKind() == FloatSymExprKind; + } +}; + /// \brief Represents a symbolic expression like 'x' + 'y'. class SymSymExpr : public BinarySymExpr { const SymExpr *LHS; @@ -409,16 +481,17 @@ void dumpToStream(raw_ostream &os) const override; - static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *lhs, - BinaryOperator::Opcode op, const SymExpr *rhs, QualType t) { - ID.AddInteger((unsigned) SymSymExprKind); + static void Profile(llvm::FoldingSetNodeID &ID, const SymExpr *lhs, + BinaryOperator::Opcode op, const SymExpr *rhs, + QualType t) { + ID.AddInteger((unsigned)SymSymExprKind); ID.AddPointer(lhs); ID.AddInteger(op); ID.AddPointer(rhs); ID.Add(t); } - void Profile(llvm::FoldingSetNodeID& ID) override { + void Profile(llvm::FoldingSetNodeID &ID) override { Profile(ID, LHS, getOpcode(), RHS, getType()); } @@ -430,37 +503,36 @@ class SymbolManager { typedef llvm::FoldingSet DataSetTy; - typedef llvm::DenseMap SymbolDependTy; + typedef llvm::DenseMap SymbolDependTy; DataSetTy DataSet; /// Stores the extra dependencies between symbols: the data should be kept /// alive as long as the key is live. SymbolDependTy SymbolDependencies; unsigned SymbolCounter; - llvm::BumpPtrAllocator& BPAlloc; + llvm::BumpPtrAllocator &BPAlloc; BasicValueFactory &BV; ASTContext &Ctx; public: SymbolManager(ASTContext &ctx, BasicValueFactory &bv, - llvm::BumpPtrAllocator& bpalloc) - : SymbolDependencies(16), SymbolCounter(0), - BPAlloc(bpalloc), BV(bv), Ctx(ctx) {} + llvm::BumpPtrAllocator &bpalloc) + : SymbolDependencies(16), SymbolCounter(0), BPAlloc(bpalloc), BV(bv), + Ctx(ctx) {} ~SymbolManager(); static bool canSymbolicate(QualType T); /// \brief Make a unique symbol for MemRegion R according to its kind. - const SymbolRegionValue* getRegionValueSymbol(const TypedValueRegion* R); + const SymbolRegionValue *getRegionValueSymbol(const TypedValueRegion *R); - const SymbolConjured* conjureSymbol(const Stmt *E, - const LocationContext *LCtx, - QualType T, + const SymbolConjured *conjureSymbol(const Stmt *E, + const LocationContext *LCtx, QualType T, unsigned VisitCount, const void *SymbolTag = nullptr); - const SymbolConjured* conjureSymbol(const Expr *E, + const SymbolConjured *conjureSymbol(const Expr *E, const LocationContext *LCtx, unsigned VisitCount, const void *SymbolTag = nullptr) { @@ -482,27 +554,28 @@ unsigned VisitCount, const void *SymbolTag = nullptr); - const SymbolCast* getCastSymbol(const SymExpr *Operand, - QualType From, QualType To); + const SymbolCast *getCastSymbol(const SymExpr *Operand, QualType From, + QualType To); const SymIntExpr *getSymIntExpr(const SymExpr *lhs, BinaryOperator::Opcode op, - const llvm::APSInt& rhs, QualType t); + const llvm::APSInt &rhs, QualType t); - const SymIntExpr *getSymIntExpr(const SymExpr &lhs, BinaryOperator::Opcode op, - const llvm::APSInt& rhs, QualType t) { - return getSymIntExpr(&lhs, op, rhs, t); - } + const IntSymExpr *getIntSymExpr(const llvm::APSInt &lhs, + BinaryOperator::Opcode op, const SymExpr *rhs, + QualType t); - const IntSymExpr *getIntSymExpr(const llvm::APSInt& lhs, - BinaryOperator::Opcode op, - const SymExpr *rhs, QualType t); + const SymFloatExpr *getSymFloatExpr(const SymExpr *lhs, + BinaryOperator::Opcode op, + const llvm::APFloat &rhs, QualType t); + + const FloatSymExpr *getFloatSymExpr(const llvm::APFloat &lhs, + BinaryOperator::Opcode op, + const SymExpr *rhs, QualType t); const SymSymExpr *getSymSymExpr(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType t); - QualType getType(const SymExpr *SE) const { - return SE->getType(); - } + QualType getType(const SymExpr *SE) const { return SE->getType(); } /// \brief Add artificial symbol dependency. /// @@ -517,10 +590,7 @@ /// \brief A class responsible for cleaning up unused symbols. class SymbolReaper { - enum SymbolStatus { - NotProcessed, - HaveMarkedDependents - }; + enum SymbolStatus { NotProcessed, HaveMarkedDependents }; typedef llvm::DenseSet SymbolSetTy; typedef llvm::DenseMap SymbolMapTy; @@ -531,10 +601,10 @@ SymbolSetTy TheDead; RegionSetTy RegionRoots; - + const StackFrameContext *LCtx; const Stmt *Loc; - SymbolManager& SymMgr; + SymbolManager &SymMgr; StoreRef reapedStore; llvm::DenseMap includedRegionCache; @@ -546,10 +616,9 @@ /// considered live. /// If the stack frame context is NULL, everything on stack is considered /// dead. - SymbolReaper(const StackFrameContext *Ctx, const Stmt *s, SymbolManager& symmgr, - StoreManager &storeMgr) - : LCtx(Ctx), Loc(s), SymMgr(symmgr), - reapedStore(nullptr, storeMgr) {} + SymbolReaper(const StackFrameContext *Ctx, const Stmt *s, + SymbolManager &symmgr, StoreManager &storeMgr) + : LCtx(Ctx), Loc(s), SymMgr(symmgr), reapedStore(nullptr, storeMgr) {} const LocationContext *getLocationContext() const { return LCtx; } @@ -584,10 +653,8 @@ dead_iterator dead_begin() const { return TheDead.begin(); } dead_iterator dead_end() const { return TheDead.end(); } - bool hasDeadSymbols() const { - return !TheDead.empty(); - } - + bool hasDeadSymbols() const { return !TheDead.empty(); } + typedef RegionSetTy::const_iterator region_iterator; region_iterator region_begin() const { return RegionRoots.begin(); } region_iterator region_end() const { return RegionRoots.end(); } @@ -596,13 +663,11 @@ /// /// This should only be called once all marking of dead symbols has completed. /// (For checkers, this means only in the evalDeadSymbols callback.) - bool isDead(SymbolRef sym) const { - return TheDead.count(sym); - } - + bool isDead(SymbolRef sym) const { return TheDead.count(sym); } + void markLive(const MemRegion *region); void markElementIndicesLive(const MemRegion *region); - + /// \brief Set to the value of the symbolic store after /// StoreManager::removeDeadBindings has been called. void setReapedStore(StoreRef st) { reapedStore = st; } @@ -621,7 +686,8 @@ SymbolVisitor(const SymbolVisitor &) = default; SymbolVisitor(SymbolVisitor &&) {} - /// \brief A visitor method invoked by ProgramStateManager::scanReachableSymbols. + /// \brief A visitor method invoked by + /// ProgramStateManager::scanReachableSymbols. /// /// The method returns \c true if symbols should continue be scanned and \c /// false otherwise. @@ -629,9 +695,9 @@ virtual bool VisitMemRegion(const MemRegion *region) { return true; } }; -} // end GR namespace +} // namespace ento -} // end clang namespace +} // namespace clang namespace llvm { static inline raw_ostream &operator<<(raw_ostream &os, @@ -639,5 +705,5 @@ SE->dumpToStream(os); return os; } -} // end llvm namespace +} // namespace llvm #endif Index: include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def +++ include/clang/StaticAnalyzer/Core/PathSensitive/Symbols.def @@ -37,6 +37,8 @@ ABSTRACT_SYMBOL(BinarySymExpr, SymExpr) SYMBOL(IntSymExpr, BinarySymExpr) SYMBOL(SymIntExpr, BinarySymExpr) + SYMBOL(FloatSymExpr, BinarySymExpr) + SYMBOL(SymFloatExpr, BinarySymExpr) SYMBOL(SymSymExpr, BinarySymExpr) SYMBOL_RANGE(BINARYSYMEXPRS, IntSymExprKind, SymSymExprKind) Index: lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp +++ lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -59,6 +59,80 @@ return true; } + case Builtin::BI__builtin_inf: + case Builtin::BI__builtin_inff: + case Builtin::BI__builtin_infl: { + SValBuilder &SVB = C.getSValBuilder(); + ASTContext &Ctx = SVB.getContext(); + llvm::APFloat Inf = llvm::APFloat::getInf( + Ctx.getFloatTypeSemantics(FD->getReturnType())); + SVal V = SVB.makeFloatVal(Inf); + C.addTransition(state->BindExpr(CE, LCtx, V)); + return true; + } + + // TODO: Model the string argument as well + case Builtin::BI__builtin_nan: + case Builtin::BI__builtin_nanf: + case Builtin::BI__builtin_nanl: { + SValBuilder &SVB = C.getSValBuilder(); + ASTContext &Ctx = SVB.getContext(); + llvm::APFloat NaN = llvm::APFloat::getQNaN( + Ctx.getFloatTypeSemantics(FD->getReturnType())); + SVal V = SVB.makeFloatVal(NaN); + C.addTransition(state->BindExpr(CE, LCtx, V)); + return true; + } + + case Builtin::BI__builtin_isinf: { + SValBuilder &SVB = C.getSValBuilder(); + ASTContext &Ctx = SVB.getContext(); + assert(CE->arg_begin() + 1 == CE->arg_end()); + DefinedOrUnknownSVal V = + state->getSVal(*(CE->arg_begin()), LCtx).castAs(); + + llvm::APFloat PInf = llvm::APFloat::getInf( + Ctx.getFloatTypeSemantics((*(CE->arg_begin()))->getType()), false); + DefinedOrUnknownSVal SPInf = SVB.makeFloatVal(PInf); + DefinedOrUnknownSVal isPInf = SVB.evalEQ(state, V, SPInf); + if (isPInf.isConstant(1)) { + C.addTransition(state->BindExpr(CE, LCtx, isPInf)); + return true; + } + + llvm::APFloat NInf = llvm::APFloat::getInf( + Ctx.getFloatTypeSemantics((*(CE->arg_begin()))->getType()), true); + DefinedOrUnknownSVal SNInf = SVB.makeFloatVal(NInf); + DefinedOrUnknownSVal isNInf = SVB.evalEQ(state, V, SNInf); + if (isPInf.isZeroConstant() && isNInf.isConstant(1)) { + C.addTransition(state->BindExpr(CE, LCtx, isNInf)); + return true; + } + + // TODO: FIXME + // This should be BO_LOr for (V == -\inf) || (V == \inf), but logical + // operations are handled much earlier during ExplodedGraph generation. + // However, since both sides are Boolean/Int1Ty, we can use bitwise or. + // If/when this is fixed, also remove the explicit short-circuits above. + SVal isInf = SVB.evalBinOp(state, BO_Or, isPInf, isNInf, + SVB.getConditionType()); + C.addTransition(state->BindExpr(CE, LCtx, isInf)); + return true; + } + + // TODO: FIXME + // IEEE-754 changes evaluation of direct comparison to NaN, which makes it + // difficult to express NaN constraints for the symbolic executor. + // As a workaround, express this as V != V, for which only NaN satisfies. + case Builtin::BI__builtin_isnan: { + SValBuilder &SVB = C.getSValBuilder(); + assert(CE->arg_begin() + 1 == CE->arg_end()); + SVal V = state->getSVal(*(CE->arg_begin()), LCtx); + SVal isNaN = SVB.evalBinOp(state, BO_NE, V, V, SVB.getConditionType()); + C.addTransition(state->BindExpr(CE, LCtx, isNaN)); + return true; + } + case Builtin::BI__builtin_unpredictable: case Builtin::BI__builtin_expect: case Builtin::BI__builtin_assume_aligned: Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -36,6 +36,7 @@ DynamicTypeChecker.cpp ExprInspectionChecker.cpp FixedAddressChecker.cpp + FloatingPointMath.cpp GenericTaintChecker.cpp GTestChecker.cpp IdenticalExprChecker.cpp Index: lib/StaticAnalyzer/Checkers/CStringChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -1851,7 +1851,7 @@ SVal lenVal = state->getSVal(lenExpr, LCtx); // If the length is known, we can get the right substrings. - if (const llvm::APSInt *len = svalBuilder.getKnownValue(state, lenVal)) { + if (const llvm::APSInt *len = svalBuilder.getKnownIntValue(state, lenVal)) { // Create substrings of each to compare the prefix. s1StrRef = s1StrRef.substr(0, (size_t)len->getZExtValue()); s2StrRef = s2StrRef.substr(0, (size_t)len->getZExtValue()); Index: lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp +++ lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp @@ -111,7 +111,7 @@ SValBuilder &svalBuilder = C.getSValBuilder(); SVal extent = SR->getExtent(svalBuilder); - const llvm::APSInt *extentInt = svalBuilder.getKnownValue(state, extent); + const llvm::APSInt *extentInt = svalBuilder.getKnownIntValue(state, extent); if (!extentInt) return; Index: lib/StaticAnalyzer/Checkers/FloatingPointMath.cpp =================================================================== --- /dev/null +++ lib/StaticAnalyzer/Checkers/FloatingPointMath.cpp @@ -0,0 +1,366 @@ +//=== FloatingPointMathChecker.cpp --------------------------------*- C++ +//-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker evaluates calls to floating-point math functions for domain +// errors. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/Basic/Builtins.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" + +using namespace clang; +using namespace ento; + +namespace { +class FloatingPointMathChecker : public Checker { + mutable std::unique_ptr BT; + + void reportError(StringRef Name, StringRef Domain, ProgramStateRef State, + CheckerContext &C) const; + + void inline handleAssumption(StringRef Name, StringRef Domain, + std::pair P, + ProgramStateRef State, CheckerContext &C) const; + + bool isNaN(SValBuilder &SVB, ProgramStateRef State, SVal V) const; + +public: + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; +}; + +} // end anonymous namespace + +void FloatingPointMathChecker::reportError(StringRef Name, StringRef Domain, + ProgramStateRef State, + CheckerContext &C) const { + if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { + if (!BT) { + BT.reset(new BuiltinBug(this, "Domain/range error")); + } + + SmallString<100> Str; + llvm::raw_svector_ostream OS(Str); + + OS << "Argument value is out of valid domain " << Domain + << " for function call to " << Name; + C.emitReport(llvm::make_unique(*BT, OS.str(), N)); + } +} + +void FloatingPointMathChecker::handleAssumption( + StringRef Name, StringRef Domain, + std::pair P, ProgramStateRef State, + CheckerContext &C) const { + ProgramStateRef StT, StF; + std::tie(StT, StF) = P; + + if (StF) { + reportError(Name, Domain, State, C); + + if (!StT) + C.addTransition(StF); + } + + if (!StF && StT) + C.addTransition(StT); +} + +bool FloatingPointMathChecker::isNaN(SValBuilder &SVB, ProgramStateRef State, + SVal V) const { + SVal notNaN = SVB.evalBinOp(State, BO_EQ, V, V, SVB.getConditionType()); + return notNaN.isUnknown() || notNaN.isZeroConstant(); +} + +void FloatingPointMathChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const Decl *D = Call.getDecl(); + if (!D) + return; + const FunctionDecl *FD = dyn_cast(D); + if (!FD) + return; + + ConstraintManager &CM = C.getConstraintManager(); + SValBuilder &SVB = C.getSValBuilder(); + BasicValueFactory &BVF = SVB.getBasicValueFactory(); + ASTContext &Ctx = SVB.getContext(); + QualType Int32Ty = Ctx.getIntTypeForBitwidth(32, true); + + StringRef Callee = FD->getName(); + switch (FD->getBuiltinID()) { + default: + return; + + // acos(x): -1 <= x <= 1 + case Builtin::BIacos: + case Builtin::BIacosf: + case Builtin::BIacosl: + case Builtin::BIasin: + case Builtin::BIasinf: + case Builtin::BIasinl: + case Builtin::BI__builtin_acos: + case Builtin::BI__builtin_acosf: + case Builtin::BI__builtin_acosl: + case Builtin::BI__builtin_asin: + case Builtin::BI__builtin_asinf: + case Builtin::BI__builtin_asinl: { + assert(Call.getNumArgs() == 1); + SVal V = Call.getArgSVal(0); + llvm::APSInt From = BVF.getValue(-1, Int32Ty); + llvm::APSInt To = BVF.getValue(1, Int32Ty); + + // Skip if known to be NaN, otherwise assume to be not NaN + if (isNaN(SVB, State, V)) + return; + + // This relies on the constraint manager promoting from APSInt to APFloat + // because the type of V is floating-point. + return handleAssumption( + Callee, "[-1, 1]", + CM.assumeInclusiveRangeDual(State, V.castAs(), From, To), State, + C); + } + + // acosh(x): x >= 1 + case Builtin::BIacosh: + case Builtin::BIacoshf: + case Builtin::BIacoshl: + case Builtin::BI__builtin_acosh: + case Builtin::BI__builtin_acoshf: + case Builtin::BI__builtin_acoshl: { + StringRef Range = "[1, inf)"; + + assert(Call.getNumArgs() == 1); + SVal V = Call.getArgSVal(0); + llvm::APFloat From = BVF.getValue( + 1, Ctx.getFloatTypeSemantics(Call.getArgExpr(0)->getType())); + DefinedOrUnknownSVal SV = SVB.makeFloatVal(From); + + if (isNaN(SVB, State, V)) + return; + + SVal isGE = SVB.evalBinOp(State, BO_GE, V, SV, SVB.getConditionType()); + if (isGE.isZeroConstant()) + return reportError(Callee, Range, State, C); + + return handleAssumption(Callee, Range, + CM.assumeDual(State, isGE.castAs()), + State, C); + } + + // atanh(x): -1 < x < 1 + case Builtin::BIatanh: + case Builtin::BIatanhf: + case Builtin::BIatanhl: + case Builtin::BI__builtin_atanh: + case Builtin::BI__builtin_atanhf: + case Builtin::BI__builtin_atanhl: { + StringRef Range = "(-1, 1)"; + + assert(Call.getNumArgs() == 1); + SVal V = Call.getArgSVal(0); + llvm::APFloat From = BVF.getValue( + -1, Ctx.getFloatTypeSemantics(Call.getArgExpr(0)->getType())); + llvm::APFloat To = BVF.getValue( + 1, Ctx.getFloatTypeSemantics(Call.getArgExpr(0)->getType())); + + if (isNaN(SVB, State, V)) + return; + + DefinedOrUnknownSVal SFrom = SVB.makeFloatVal(From); + SVal isGT = SVB.evalBinOp(State, BO_GT, V, SFrom, SVB.getConditionType()); + if (isGT.isZeroConstant()) + return reportError(Callee, Range, State, C); + + DefinedOrUnknownSVal STo = SVB.makeFloatVal(To); + SVal isLT = SVB.evalBinOp(State, BO_LT, V, STo, SVB.getConditionType()); + if (isGT.isConstant(1) && isLT.isZeroConstant()) + return reportError(Callee, Range, State, C); + + // TODO: FIXME + // This should be BO_LAnd, but logical operations are handled much earlier + // during ExplodedGraph generation. However, since both sides are + // Boolean/Int1Ty, we can use bitwise and. + // If/when this is fixed, also remove the explicit short-circuits above. + SVal isIR = + SVB.evalBinOp(State, BO_And, isGT, isLT, SVB.getConditionType()); + if (isIR.isZeroConstant()) + return reportError(Callee, Range, State, C); + + return handleAssumption(Callee, Range, + CM.assumeDual(State, isIR.castAs()), + State, C); + } + + // log(x): x >= 0 + case Builtin::BIlog: + case Builtin::BIlogf: + case Builtin::BIlogl: + case Builtin::BIlog2: + case Builtin::BIlog2f: + case Builtin::BIlog2l: + case Builtin::BIlog10: + case Builtin::BIlog10f: + case Builtin::BIlog10l: + case Builtin::BIsqrt: + case Builtin::BIsqrtf: + case Builtin::BIsqrtl: + case Builtin::BI__builtin_log: + case Builtin::BI__builtin_logf: + case Builtin::BI__builtin_logl: + case Builtin::BI__builtin_log2: + case Builtin::BI__builtin_log2f: + case Builtin::BI__builtin_log2l: + case Builtin::BI__builtin_log10: + case Builtin::BI__builtin_log10f: + case Builtin::BI__builtin_log10l: + case Builtin::BI__builtin_sqrt: + case Builtin::BI__builtin_sqrtf: + case Builtin::BI__builtin_sqrtl: { + StringRef Range = "[0, inf)"; + + assert(Call.getNumArgs() == 1); + SVal V = Call.getArgSVal(0); + llvm::APFloat From = BVF.getValue( + 0, Ctx.getFloatTypeSemantics(Call.getArgExpr(0)->getType())); + DefinedOrUnknownSVal SV = SVB.makeFloatVal(From); + + if (isNaN(SVB, State, V)) + return; + + SVal isGE = SVB.evalBinOp(State, BO_GE, V, SV, SVB.getConditionType()); + if (isGE.isZeroConstant()) + return reportError(Callee, Range, State, C); + + return handleAssumption(Callee, Range, + CM.assumeDual(State, isGE.castAs()), + State, C); + } + + // log1p(x): x >= -1 + case Builtin::BIlog1p: + case Builtin::BIlog1pf: + case Builtin::BIlog1pl: + case Builtin::BI__builtin_log1p: + case Builtin::BI__builtin_log1pf: + case Builtin::BI__builtin_log1pl: { + StringRef Range = "[-1, inf)"; + + assert(Call.getNumArgs() == 1); + SVal V = Call.getArgSVal(0); + llvm::APFloat From = BVF.getValue( + -1, Ctx.getFloatTypeSemantics(Call.getArgExpr(0)->getType())); + DefinedOrUnknownSVal SV = SVB.makeFloatVal(From); + + if (isNaN(SVB, State, V)) + return; + + SVal isGE = SVB.evalBinOp(State, BO_GE, V, SV, SVB.getConditionType()); + if (isGE.isZeroConstant()) + return reportError(Callee, Range, State, C); + + return handleAssumption(Callee, Range, + CM.assumeDual(State, isGE.castAs()), + State, C); + } + + // logb(x): x != 0 + case Builtin::BIlogb: + case Builtin::BIlogbf: + case Builtin::BIlogbl: + case Builtin::BI__builtin_logb: + case Builtin::BI__builtin_logbf: + case Builtin::BI__builtin_logbl: { + StringRef Range = "(-inf, 0) U (0, inf)"; + + assert(Call.getNumArgs() == 1); + SVal V = Call.getArgSVal(0); + llvm::APFloat From = BVF.getValue( + 0, Ctx.getFloatTypeSemantics(Call.getArgExpr(0)->getType())); + DefinedOrUnknownSVal SV = SVB.makeFloatVal(From); + + SVal isNE = SVB.evalBinOp(State, BO_NE, V, SV, SVB.getConditionType()); + if (isNE.isZeroConstant()) + return reportError(Callee, Range, State, C); + + return handleAssumption(Callee, Range, + CM.assumeDual(State, isNE.castAs()), + State, C); + } + + // TODO: + // pow(x, y) : x > 0 || (x == 0 && y > 0) || (x < 0 && (int) y) + // case Builtin::BIpow: + // case Builtin::BIpowf: + // case Builtin::BIpowl: + // case Builtin::BI__builtin_pow: + // case Builtin::BI__builtin_powf: + // case Builtin::BI__builtin_powl: + + // TODO: + // lgamma(x) : x != 0 && !(x < 0 && (int) x) + // case Builtin::BIlgamma: + // case Builtin::BIlgammaf: + // case Builtin::BIlgammal: + // case Builtin::BItgamma: + // case Builtin::BItgammaf: + // case Builtin::BItgammal: + // case Builtin::BI__builtin_lgamma: + // case Builtin::BI__builtin_lgammaf: + // case Builtin::BI__builtin_lgammal: + // case Builtin::BI__builtin_tgamma: + // case Builtin::BI__builtin_tgammaf: + // case Builtin::BI__builtin_tgammal: + + // fmod(x,y) : y != 0 + case Builtin::BIfmod: + case Builtin::BIfmodf: + case Builtin::BIfmodl: + case Builtin::BIremainder: + case Builtin::BIremainderf: + case Builtin::BIremainderl: + case Builtin::BI__builtin_fmod: + case Builtin::BI__builtin_fmodf: + case Builtin::BI__builtin_fmodl: + case Builtin::BI__builtin_remainder: + case Builtin::BI__builtin_remainderf: + case Builtin::BI__builtin_remainderl: + case Builtin::BI__builtin_remquo: + case Builtin::BI__builtin_remquof: + case Builtin::BI__builtin_remquol: { + StringRef Range = "(-inf, 0) U (0, inf)"; + + assert(Call.getNumArgs() >= 2); + SVal V = Call.getArgSVal(1); + llvm::APFloat From = BVF.getValue( + 0, Ctx.getFloatTypeSemantics(Call.getArgExpr(1)->getType())); + DefinedOrUnknownSVal SV = SVB.makeFloatVal(From); + + SVal isNE = SVB.evalBinOp(State, BO_NE, V, SV, SVB.getConditionType()); + if (isNE.isZeroConstant()) + return reportError(Callee, Range, State, C); + + return handleAssumption(Callee, Range, + CM.assumeDual(State, isNE.castAs()), + State, C); + } + } +} + +void ento::registerFloatingPointMathChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} Index: lib/StaticAnalyzer/Core/BasicValueFactory.cpp =================================================================== --- lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -72,6 +72,9 @@ for (APSIntSetTy::iterator I=APSIntSet.begin(), E=APSIntSet.end(); I!=E; ++I) I->getValue().~APSInt(); + for (APFloatSetTy::iterator I=APFloatSet.begin(), E=APFloatSet.end(); I!=E; ++I) + I->getValue().~APFloat(); + delete (PersistentSValsTy*) PersistentSVals; delete (PersistentSValPairsTy*) PersistentSValPairs; } @@ -111,6 +114,28 @@ return getValue(getAPSIntType(T).getValue(X)); } +const llvm::APFloat &BasicValueFactory::getValue(const llvm::APFloat& X) { + llvm::FoldingSetNodeID ID; + void *InsertPos; + typedef llvm::FoldingSetNodeWrapper FoldNodeTy; + + X.Profile(ID); + FoldNodeTy* P = APFloatSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!P) { + P = (FoldNodeTy*) BPAlloc.Allocate(); + new (P) FoldNodeTy(X); + APFloatSet.InsertNode(P, InsertPos); + } + + return *P; +} + +const llvm::APFloat &BasicValueFactory::getValue(int64_t X, + const llvm::fltSemantics &S) { + return Convert(llvm::APSInt::get(X), S); +} + const CompoundValData* BasicValueFactory::getCompoundValData(QualType T, llvm::ImmutableList Vals) { @@ -198,7 +223,7 @@ switch (Op) { default: - assert (false && "Invalid Opcode."); + llvm_unreachable("Unrecognized opcode!"); case BO_Mul: return &getValue( V1 * V2 ); @@ -286,6 +311,79 @@ } } +const llvm::APFloat* +BasicValueFactory::evalAPFloat(BinaryOperator::Opcode Op, + const llvm::APFloat& V1, const llvm::APFloat& V2) { + llvm::APFloat NewV = V1; + llvm::APFloat::opStatus Status = llvm::APFloat::opOK; + + switch (Op) { + default: + llvm_unreachable("Unrecognized opcode!"); + + case BO_Mul: + Status = NewV.multiply(V2, llvm::APFloat::rmNearestTiesToEven); + if (Status & llvm::APFloat::opOK) + return &getValue(NewV); + break; + + case BO_Div: + Status = NewV.divide(V2, llvm::APFloat::rmNearestTiesToEven); + if (Status & llvm::APFloat::opOK) + return &getValue(NewV); + break; + + case BO_Rem: + Status = NewV.remainder(V2); + if (Status & llvm::APFloat::opOK) + return &getValue(NewV); + break; + + case BO_Add: + Status = NewV.add(V2, llvm::APFloat::rmNearestTiesToEven); + if (Status & llvm::APFloat::opOK) + return &getValue(NewV); + break; + + case BO_Sub: + Status = NewV.subtract(V2, llvm::APFloat::rmNearestTiesToEven); + if (Status & llvm::APFloat::opOK) + return &getValue(NewV); + break; + } + + return &getValue(NewV); +} + +const llvm::APSInt* +BasicValueFactory::evalAPFloatComparison(BinaryOperator::Opcode Op, + const llvm::APFloat& V1, const llvm::APFloat& V2) { + llvm::APFloat::cmpResult R = V1.compare(V2); + switch (Op) { + default: + llvm_unreachable("Unrecognized opcode!"); + + case BO_LT: + return &getTruthValue(R == llvm::APFloat::cmpLessThan); + + case BO_GT: + return &getTruthValue(R == llvm::APFloat::cmpGreaterThan); + + case BO_LE: + return &getTruthValue(R == llvm::APFloat::cmpLessThan || + R == llvm::APFloat::cmpEqual); + + case BO_GE: + return &getTruthValue(R == llvm::APFloat::cmpGreaterThan || + R == llvm::APFloat::cmpEqual); + + case BO_EQ: + return &getTruthValue(R == llvm::APFloat::cmpEqual); + + case BO_NE: + return &getTruthValue(R != llvm::APFloat::cmpEqual); + } +} const std::pair& BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) { Index: lib/StaticAnalyzer/Core/Environment.cpp =================================================================== --- lib/StaticAnalyzer/Core/Environment.cpp +++ lib/StaticAnalyzer/Core/Environment.cpp @@ -86,6 +86,7 @@ case Stmt::CXXScalarValueInitExprClass: case Stmt::ImplicitValueInitExprClass: case Stmt::IntegerLiteralClass: + case Stmt::FloatingLiteralClass: case Stmt::ObjCBoolLiteralExprClass: case Stmt::CXXNullPtrLiteralExprClass: case Stmt::ObjCStringLiteralClass: Index: lib/StaticAnalyzer/Core/ProgramState.cpp =================================================================== --- lib/StaticAnalyzer/Core/ProgramState.cpp +++ lib/StaticAnalyzer/Core/ProgramState.cpp @@ -22,17 +22,18 @@ using namespace clang; using namespace ento; -namespace clang { namespace ento { +namespace clang { +namespace ento { /// Increments the number of times this state is referenced. void ProgramStateRetain(const ProgramState *state) { - ++const_cast(state)->refCount; + ++const_cast(state)->refCount; } /// Decrement the number of times this state is referenced. void ProgramStateRelease(const ProgramState *state) { assert(state->refCount > 0); - ProgramState *s = const_cast(state); + ProgramState *s = const_cast(state); if (--s->refCount == 0) { ProgramStateManager &Mgr = s->getStateManager(); Mgr.StateSet.RemoveNode(s); @@ -40,25 +41,18 @@ Mgr.freeStates.push_back(s); } } -}} +} // namespace ento +} // namespace clang -ProgramState::ProgramState(ProgramStateManager *mgr, const Environment& env, - StoreRef st, GenericDataMap gdm) - : stateMgr(mgr), - Env(env), - store(st.getStore()), - GDM(gdm), - refCount(0) { +ProgramState::ProgramState(ProgramStateManager *mgr, const Environment &env, + StoreRef st, GenericDataMap gdm) + : stateMgr(mgr), Env(env), store(st.getStore()), GDM(gdm), refCount(0) { stateMgr->getStoreManager().incrementReferenceCount(store); } ProgramState::ProgramState(const ProgramState &RHS) - : llvm::FoldingSetNode(), - stateMgr(RHS.stateMgr), - Env(RHS.Env), - store(RHS.store), - GDM(RHS.GDM), - refCount(0) { + : llvm::FoldingSetNode(), stateMgr(RHS.stateMgr), Env(RHS.Env), + store(RHS.store), GDM(RHS.GDM), refCount(0) { stateMgr->getStoreManager().incrementReferenceCount(store); } @@ -72,24 +66,23 @@ ConstraintManagerCreator CreateCMgr, llvm::BumpPtrAllocator &alloc, SubEngine *SubEng) - : Eng(SubEng), EnvMgr(alloc), GDMFactory(alloc), - svalBuilder(createSimpleSValBuilder(alloc, Ctx, *this)), - CallEventMgr(new CallEventManager(alloc)), Alloc(alloc) { + : Eng(SubEng), EnvMgr(alloc), GDMFactory(alloc), + svalBuilder(createSimpleSValBuilder(alloc, Ctx, *this)), + CallEventMgr(new CallEventManager(alloc)), Alloc(alloc) { StoreMgr = (*CreateSMgr)(*this); ConstraintMgr = (*CreateCMgr)(*this, SubEng); } - ProgramStateManager::~ProgramStateManager() { - for (GDMContextsTy::iterator I=GDMContexts.begin(), E=GDMContexts.end(); - I!=E; ++I) + for (GDMContextsTy::iterator I = GDMContexts.begin(), E = GDMContexts.end(); + I != E; ++I) I->second.second(I->second.first); } ProgramStateRef ProgramStateManager::removeDeadBindings(ProgramStateRef state, - const StackFrameContext *LCtx, - SymbolReaper& SymReaper) { + const StackFrameContext *LCtx, + SymbolReaper &SymReaper) { // This code essentially performs a "mark-and-sweep" of the VariableBindings. // The roots are any Block-level exprs and Decls that our liveness algorithm @@ -102,8 +95,8 @@ NewState.Env = EnvMgr.removeDeadBindings(NewState.Env, SymReaper, state); // Clean up the store. - StoreRef newStore = StoreMgr->removeDeadBindings(NewState.getStore(), LCtx, - SymReaper); + StoreRef newStore = + StoreMgr->removeDeadBindings(NewState.getStore(), LCtx, SymReaper); NewState.setStore(newStore); SymReaper.setReapedStore(newStore); @@ -111,13 +104,12 @@ return ConstraintMgr->removeDeadBindings(Result, SymReaper); } -ProgramStateRef ProgramState::bindLoc(Loc LV, - SVal V, +ProgramStateRef ProgramState::bindLoc(Loc LV, SVal V, const LocationContext *LCtx, bool notifyChanges) const { ProgramStateManager &Mgr = getStateManager(); - ProgramStateRef newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(), - LV, V)); + ProgramStateRef newState = + makeWithStore(Mgr.StoreMgr->Bind(getStore(), LV, V)); const MemRegion *MR = LV.getAsRegion(); if (MR && Mgr.getOwningEngine() && notifyChanges) return Mgr.getOwningEngine()->processRegionChange(newState, MR, LCtx); @@ -125,61 +117,51 @@ return newState; } -ProgramStateRef ProgramState::bindDefault(SVal loc, - SVal V, +ProgramStateRef ProgramState::bindDefault(SVal loc, SVal V, const LocationContext *LCtx) const { ProgramStateManager &Mgr = getStateManager(); const MemRegion *R = loc.castAs().getRegion(); const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V); ProgramStateRef new_state = makeWithStore(newStore); - return Mgr.getOwningEngine() ? - Mgr.getOwningEngine()->processRegionChange(new_state, R, LCtx) : - new_state; + return Mgr.getOwningEngine() + ? Mgr.getOwningEngine()->processRegionChange(new_state, R, LCtx) + : new_state; } typedef ArrayRef RegionList; typedef ArrayRef ValueList; -ProgramStateRef -ProgramState::invalidateRegions(RegionList Regions, - const Expr *E, unsigned Count, - const LocationContext *LCtx, - bool CausedByPointerEscape, - InvalidatedSymbols *IS, - const CallEvent *Call, - RegionAndSymbolInvalidationTraits *ITraits) const { +ProgramStateRef ProgramState::invalidateRegions( + RegionList Regions, const Expr *E, unsigned Count, + const LocationContext *LCtx, bool CausedByPointerEscape, + InvalidatedSymbols *IS, const CallEvent *Call, + RegionAndSymbolInvalidationTraits *ITraits) const { SmallVector Values; - for (RegionList::const_iterator I = Regions.begin(), - End = Regions.end(); I != End; ++I) + for (RegionList::const_iterator I = Regions.begin(), End = Regions.end(); + I != End; ++I) Values.push_back(loc::MemRegionVal(*I)); return invalidateRegionsImpl(Values, E, Count, LCtx, CausedByPointerEscape, IS, ITraits, Call); } -ProgramStateRef -ProgramState::invalidateRegions(ValueList Values, - const Expr *E, unsigned Count, - const LocationContext *LCtx, - bool CausedByPointerEscape, - InvalidatedSymbols *IS, - const CallEvent *Call, - RegionAndSymbolInvalidationTraits *ITraits) const { +ProgramStateRef ProgramState::invalidateRegions( + ValueList Values, const Expr *E, unsigned Count, + const LocationContext *LCtx, bool CausedByPointerEscape, + InvalidatedSymbols *IS, const CallEvent *Call, + RegionAndSymbolInvalidationTraits *ITraits) const { return invalidateRegionsImpl(Values, E, Count, LCtx, CausedByPointerEscape, IS, ITraits, Call); } -ProgramStateRef -ProgramState::invalidateRegionsImpl(ValueList Values, - const Expr *E, unsigned Count, - const LocationContext *LCtx, - bool CausedByPointerEscape, - InvalidatedSymbols *IS, - RegionAndSymbolInvalidationTraits *ITraits, - const CallEvent *Call) const { +ProgramStateRef ProgramState::invalidateRegionsImpl( + ValueList Values, const Expr *E, unsigned Count, + const LocationContext *LCtx, bool CausedByPointerEscape, + InvalidatedSymbols *IS, RegionAndSymbolInvalidationTraits *ITraits, + const CallEvent *Call) const { ProgramStateManager &Mgr = getStateManager(); - SubEngine* Eng = Mgr.getOwningEngine(); + SubEngine *Eng = Mgr.getOwningEngine(); InvalidatedSymbols Invalidated; if (!IS) @@ -192,18 +174,15 @@ if (Eng) { StoreManager::InvalidatedRegions TopLevelInvalidated; StoreManager::InvalidatedRegions Invalidated; - const StoreRef &newStore - = Mgr.StoreMgr->invalidateRegions(getStore(), Values, E, Count, LCtx, Call, - *IS, *ITraits, &TopLevelInvalidated, - &Invalidated); + const StoreRef &newStore = Mgr.StoreMgr->invalidateRegions( + getStore(), Values, E, Count, LCtx, Call, *IS, *ITraits, + &TopLevelInvalidated, &Invalidated); ProgramStateRef newState = makeWithStore(newStore); if (CausedByPointerEscape) { - newState = Eng->notifyCheckersOfPointerEscape(newState, IS, - TopLevelInvalidated, - Invalidated, Call, - *ITraits); + newState = Eng->notifyCheckersOfPointerEscape( + newState, IS, TopLevelInvalidated, Invalidated, Call, *ITraits); } return Eng->processRegionChanges(newState, IS, TopLevelInvalidated, @@ -211,8 +190,8 @@ } const StoreRef &newStore = - Mgr.StoreMgr->invalidateRegions(getStore(), Values, E, Count, LCtx, Call, - *IS, *ITraits, nullptr, nullptr); + Mgr.StoreMgr->invalidateRegions(getStore(), Values, E, Count, LCtx, Call, + *IS, *ITraits, nullptr, nullptr); return makeWithStore(newStore); } @@ -221,7 +200,7 @@ Store OldStore = getStore(); const StoreRef &newStore = - getStateManager().StoreMgr->killBinding(OldStore, LV); + getStateManager().StoreMgr->killBinding(OldStore, LV); if (newStore.getStore() == OldStore) return this; @@ -233,7 +212,7 @@ ProgramState::enterStackFrame(const CallEvent &Call, const StackFrameContext *CalleeCtx) const { const StoreRef &NewStore = - getStateManager().StoreMgr->enterStackFrame(getStore(), Call, CalleeCtx); + getStateManager().StoreMgr->enterStackFrame(getStore(), Call, CalleeCtx); return makeWithStore(NewStore); } @@ -262,29 +241,39 @@ // about). if (!T.isNull()) { if (SymbolRef sym = V.getAsSymbol()) { - if (const llvm::APSInt *Int = getStateManager() - .getConstraintManager() - .getSymVal(this, sym)) { - // FIXME: Because we don't correctly model (yet) sign-extension - // and truncation of symbolic values, we need to convert - // the integer value to the correct signedness and bitwidth. - // - // This shows up in the following: - // - // char foo(); - // unsigned x = foo(); - // if (x == 54) - // ... - // - // The symbolic value stored to 'x' is actually the conjured - // symbol for the call to foo(); the type of that symbol is 'char', - // not unsigned. - const llvm::APSInt &NewV = getBasicVals().Convert(T, *Int); - - if (V.getAs()) - return loc::ConcreteInt(NewV); - else - return nonloc::ConcreteInt(NewV); + ConstraintManager &CM = getStateManager().getConstraintManager(); + + if (sym->getType()->isRealFloatingType()) { + if (const llvm::APFloat *Float = CM.getSymFloatVal(this, sym)) { + // FIXME: Because we don't correctly model (yet) sign-extension + // and truncation of symbolic values, we need to convert + // the integer value to the correct signedness and bitwidth. + // + // This shows up in the following: + // + // char foo(); + // unsigned x = foo(); + // if (x == 54) + // ... + // + // The symbolic value stored to 'x' is actually the conjured + // symbol for the call to foo(); the type of that symbol is 'char', + // not unsigned. + const llvm::APFloat &NewV = getBasicVals().Convert(T, *Float); + + assert(!V.getAs() && "Loc::ConcreteFloat not supported!"); + return nonloc::ConcreteFloat(NewV); + } + } else { + if (const llvm::APSInt *Int = CM.getSymIntVal(this, sym)) { + // FIXME: Likewise + const llvm::APSInt &NewV = getBasicVals().Convert(T, *Int); + + if (V.getAs()) + return loc::ConcreteInt(NewV); + else + return nonloc::ConcreteInt(NewV); + } } } } @@ -293,11 +282,10 @@ } ProgramStateRef ProgramState::BindExpr(const Stmt *S, - const LocationContext *LCtx, - SVal V, bool Invalidate) const{ - Environment NewEnv = - getStateManager().EnvMgr.bindExpr(Env, EnvironmentEntry(S, LCtx), V, - Invalidate); + const LocationContext *LCtx, SVal V, + bool Invalidate) const { + Environment NewEnv = getStateManager().EnvMgr.bindExpr( + Env, EnvironmentEntry(S, LCtx), V, Invalidate); if (NewEnv == Env) return this; @@ -307,9 +295,9 @@ } ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, - DefinedOrUnknownSVal UpperBound, - bool Assumption, - QualType indexTy) const { + DefinedOrUnknownSVal UpperBound, + bool Assumption, + QualType indexTy) const { if (Idx.isUnknown() || UpperBound.isUnknown()) return this; @@ -328,15 +316,14 @@ nonloc::ConcreteInt Min(BVF.getMinValue(indexTy)); // Adjust the index. - SVal newIdx = svalBuilder.evalBinOpNN(this, BO_Add, - Idx.castAs(), Min, indexTy); + SVal newIdx = + svalBuilder.evalBinOpNN(this, BO_Add, Idx.castAs(), Min, indexTy); if (newIdx.isUnknownOrUndef()) return this; // Adjust the upper bound. - SVal newBound = - svalBuilder.evalBinOpNN(this, BO_Add, UpperBound.castAs(), - Min, indexTy); + SVal newBound = svalBuilder.evalBinOpNN( + this, BO_Add, UpperBound.castAs(), Min, indexTy); if (newBound.isUnknownOrUndef()) return this; @@ -366,18 +353,18 @@ return getStateManager().ConstraintMgr->isNull(this, Sym); } -ProgramStateRef ProgramStateManager::getInitialState(const LocationContext *InitLoc) { - ProgramState State(this, - EnvMgr.getInitialEnvironment(), - StoreMgr->getInitialStore(InitLoc), - GDMFactory.getEmptyMap()); +ProgramStateRef +ProgramStateManager::getInitialState(const LocationContext *InitLoc) { + ProgramState State(this, EnvMgr.getInitialEnvironment(), + StoreMgr->getInitialStore(InitLoc), + GDMFactory.getEmptyMap()); return getPersistentState(State); } -ProgramStateRef ProgramStateManager::getPersistentStateWithGDM( - ProgramStateRef FromState, - ProgramStateRef GDMState) { +ProgramStateRef +ProgramStateManager::getPersistentStateWithGDM(ProgramStateRef FromState, + ProgramStateRef GDMState) { ProgramState NewState(*FromState); NewState.GDM = GDMState->GDM; return getPersistentState(NewState); @@ -396,9 +383,8 @@ if (!freeStates.empty()) { newState = freeStates.back(); freeStates.pop_back(); - } - else { - newState = (ProgramState*) Alloc.Allocate(); + } else { + newState = (ProgramState *)Alloc.Allocate(); } new (newState) ProgramState(State); StateSet.InsertNode(newState, InsertPos); @@ -424,8 +410,8 @@ // State pretty-printing. //===----------------------------------------------------------------------===// -void ProgramState::print(raw_ostream &Out, - const char *NL, const char *Sep) const { +void ProgramState::print(raw_ostream &Out, const char *NL, + const char *Sep) const { // Print the store. ProgramStateManager &Mgr = getStateManager(); Mgr.getStoreManager().print(getStore(), Out, NL, Sep); @@ -444,40 +430,33 @@ print(Out, "\\l", "\\|"); } -LLVM_DUMP_METHOD void ProgramState::dump() const { - print(llvm::errs()); -} +LLVM_DUMP_METHOD void ProgramState::dump() const { print(llvm::errs()); } -void ProgramState::printTaint(raw_ostream &Out, - const char *NL, const char *Sep) const { +void ProgramState::printTaint(raw_ostream &Out, const char *NL, + const char *Sep) const { TaintMapImpl TM = get(); if (!TM.isEmpty()) - Out <<"Tainted Symbols:" << NL; + Out << "Tainted Symbols:" << NL; for (TaintMapImpl::iterator I = TM.begin(), E = TM.end(); I != E; ++I) { Out << I->first << " : " << I->second << NL; } } -void ProgramState::dumpTaint() const { - printTaint(llvm::errs()); -} +void ProgramState::dumpTaint() const { printTaint(llvm::errs()); } //===----------------------------------------------------------------------===// // Generic Data Map. //===----------------------------------------------------------------------===// -void *const* ProgramState::FindGDM(void *K) const { - return GDM.lookup(K); -} +void *const *ProgramState::FindGDM(void *K) const { return GDM.lookup(K); } -void* -ProgramStateManager::FindGDMContext(void *K, - void *(*CreateContext)(llvm::BumpPtrAllocator&), - void (*DeleteContext)(void*)) { +void *ProgramStateManager::FindGDMContext( + void *K, void *(*CreateContext)(llvm::BumpPtrAllocator &), + void (*DeleteContext)(void *)) { - std::pair& p = GDMContexts[K]; + std::pair &p = GDMContexts[K]; if (!p.first) { p.first = CreateContext(Alloc); p.second = DeleteContext; @@ -486,7 +465,8 @@ return p.first; } -ProgramStateRef ProgramStateManager::addGDM(ProgramStateRef St, void *Key, void *Data){ +ProgramStateRef ProgramStateManager::addGDM(ProgramStateRef St, void *Key, + void *Data) { ProgramState::GenericDataMap M1 = St->getGDM(); ProgramState::GenericDataMap M2 = GDMFactory.add(M1, Key, Data); @@ -498,7 +478,8 @@ return getPersistentState(NewSt); } -ProgramStateRef ProgramStateManager::removeGDM(ProgramStateRef state, void *Key) { +ProgramStateRef ProgramStateManager::removeGDM(ProgramStateRef state, + void *Key) { ProgramState::GenericDataMap OldM = state->getGDM(); ProgramState::GenericDataMap NewM = GDMFactory.remove(OldM, Key); @@ -524,7 +505,8 @@ } bool ScanReachableSymbols::scan(nonloc::CompoundVal val) { - for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I) + for (nonloc::CompoundVal::iterator I = val.begin(), E = val.end(); I != E; + ++I) if (!scan(*I)) return false; @@ -603,7 +585,7 @@ if (const BlockDataRegion *BDR = dyn_cast(R)) { BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(), E = BDR->referenced_vars_end(); - for ( ; I != E; ++I) { + for (; I != E; ++I) { if (!scan(I.getCapturedRegion())) return false; } @@ -612,26 +594,27 @@ return true; } -bool ProgramState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const { +bool ProgramState::scanReachableSymbols(SVal val, + SymbolVisitor &visitor) const { ScanReachableSymbols S(this, visitor); return S.scan(val); } bool ProgramState::scanReachableSymbols(const SVal *I, const SVal *E, - SymbolVisitor &visitor) const { + SymbolVisitor &visitor) const { ScanReachableSymbols S(this, visitor); - for ( ; I != E; ++I) { + for (; I != E; ++I) { if (!S.scan(*I)) return false; } return true; } -bool ProgramState::scanReachableSymbols(const MemRegion * const *I, - const MemRegion * const *E, - SymbolVisitor &visitor) const { +bool ProgramState::scanReachableSymbols(const MemRegion *const *I, + const MemRegion *const *E, + SymbolVisitor &visitor) const { ScanReachableSymbols S(this, visitor); - for ( ; I != E; ++I) { + for (; I != E; ++I) { if (!S.scan(*I)) return false; } @@ -639,16 +622,15 @@ } ProgramStateRef ProgramState::addTaint(const Stmt *S, - const LocationContext *LCtx, - TaintTagType Kind) const { + const LocationContext *LCtx, + TaintTagType Kind) const { if (const Expr *E = dyn_cast_or_null(S)) S = E->IgnoreParens(); return addTaint(getSVal(S, LCtx), Kind); } -ProgramStateRef ProgramState::addTaint(SVal V, - TaintTagType Kind) const { +ProgramStateRef ProgramState::addTaint(SVal V, TaintTagType Kind) const { SymbolRef Sym = V.getAsSymbol(); if (Sym) return addTaint(Sym, Kind); @@ -663,7 +645,8 @@ // their parent region, which is a conjured symbol default-bound to the base // region of the parent region. if (auto LCV = V.getAs()) { - if (Optional binding = getStateManager().StoreMgr->getDefaultBinding(*LCV)) { + if (Optional binding = + getStateManager().StoreMgr->getDefaultBinding(*LCV)) { if (SymbolRef Sym = binding->getAsSymbol()) return addPartialTaint(Sym, LCV->getRegion(), Kind); } @@ -674,14 +657,13 @@ } ProgramStateRef ProgramState::addTaint(const MemRegion *R, - TaintTagType Kind) const { + TaintTagType Kind) const { if (const SymbolicRegion *SR = dyn_cast_or_null(R)) return addTaint(SR->getSymbol(), Kind); return this; } -ProgramStateRef ProgramState::addTaint(SymbolRef Sym, - TaintTagType Kind) const { +ProgramStateRef ProgramState::addTaint(SymbolRef Sym, TaintTagType Kind) const { // If this is a symbol cast, remove the cast before adding the taint. Taint // is cast agnostic. while (const SymbolCast *SC = dyn_cast(Sym)) @@ -753,7 +735,8 @@ return false; // Traverse all the symbols this symbol depends on to see if any are tainted. - for (SymExpr::symbol_iterator SI = Sym->symbol_begin(), SE =Sym->symbol_end(); + for (SymExpr::symbol_iterator SI = Sym->symbol_begin(), + SE = Sym->symbol_end(); SI != SE; ++SI) { if (!isa(*SI)) continue; @@ -779,8 +762,7 @@ // complete. For example, this would not currently identify // overlapping fields in a union as tainted. To identify this we can // check for overlapping/nested byte offsets. - if (Kind == I.second && - (R == I.first || R->isSubRegionOf(I.first))) + if (Kind == I.second && (R == I.first || R->isSubRegionOf(I.first))) return true; } } @@ -801,4 +783,3 @@ return false; } - Index: lib/StaticAnalyzer/Core/RangeConstraintManager.cpp =================================================================== --- lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -293,10 +293,12 @@ bool canReasonAbout(SVal X) const override; + bool canReasonAboutFloats() const override; + ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override; - const llvm::APSInt *getSymVal(ProgramStateRef State, - SymbolRef Sym) const override; + const llvm::APSInt *getSymIntVal(ProgramStateRef State, + SymbolRef Sym) const override; ProgramStateRef removeDeadBindings(ProgramStateRef State, SymbolReaper &SymReaper) override; @@ -375,6 +377,9 @@ const SymExpr *SE = SymVal->getSymbol(); do { + if (SE->getType()->isFloatingType()) + return false; + if (isa(SE)) { return true; } @@ -425,6 +430,8 @@ return true; } +bool RangeConstraintManager::canReasonAboutFloats() const { return false; } + ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State, SymbolRef Sym) { const RangeSet *Ranges = State->get(Sym); @@ -449,8 +456,8 @@ return ConditionTruthVal(); } -const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St, - SymbolRef Sym) const { +const llvm::APSInt *RangeConstraintManager::getSymIntVal(ProgramStateRef St, + SymbolRef Sym) const { const ConstraintRangeTy::data_type *T = St->get(Sym); return T ? T->getConcreteValue() : nullptr; } Index: lib/StaticAnalyzer/Core/RangedConstraintManager.cpp =================================================================== --- lib/StaticAnalyzer/Core/RangedConstraintManager.cpp +++ lib/StaticAnalyzer/Core/RangedConstraintManager.cpp @@ -39,7 +39,6 @@ return assumeSymRel(State, SIE->getLHS(), op, SIE->getRHS()); } - } else if (const SymSymExpr *SSE = dyn_cast(Sym)) { // Translate "a != b" to "(b - a) != 0". // We invert the order of the operands as a heuristic for how loop Index: lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- lib/StaticAnalyzer/Core/RegionStore.cpp +++ lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1309,7 +1309,7 @@ const MemRegion *R, QualType EleTy) { SVal Size = cast(R)->getExtent(svalBuilder); - const llvm::APSInt *SizeInt = svalBuilder.getKnownValue(state, Size); + const llvm::APSInt *SizeInt = svalBuilder.getKnownIntValue(state, Size); if (!SizeInt) return UnknownVal(); @@ -2041,7 +2041,7 @@ if (Loc::isLocType(T)) V = svalBuilder.makeNull(); - else if (T->isIntegralOrEnumerationType()) + else if (T->isIntegralOrEnumerationType() || T->isRealFloatingType()) V = svalBuilder.makeZeroVal(T); else if (T->isStructureOrClassType() || T->isArrayType()) { // Set the default value to a zero constant when it is a structure Index: lib/StaticAnalyzer/Core/SValBuilder.cpp =================================================================== --- lib/StaticAnalyzer/Core/SValBuilder.cpp +++ lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -27,7 +27,7 @@ // Basic SVal creation. //===----------------------------------------------------------------------===// -void SValBuilder::anchor() { } +void SValBuilder::anchor() {} DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) { if (Loc::isLocType(type)) @@ -40,12 +40,14 @@ type->isAnyComplexType()) return makeCompoundVal(type, BasicVals.getEmptySValList()); - // FIXME: Handle floats. + if (type->isRealFloatingType()) + return makeFloatVal(0, type); + return UnknownVal(); } NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, - const llvm::APSInt& rhs, QualType type) { + const llvm::APSInt &rhs, QualType type) { // The Environment ensures we always get a persistent APSInt in // BasicValueFactory, so we don't need to get the APSInt from // BasicValueFactory again. @@ -54,7 +56,7 @@ return nonloc::SymbolVal(SymMgr.getSymIntExpr(lhs, op, rhs, type)); } -NonLoc SValBuilder::makeNonLoc(const llvm::APSInt& lhs, +NonLoc SValBuilder::makeNonLoc(const llvm::APSInt &lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType type) { assert(rhs); @@ -63,14 +65,32 @@ } NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, + const llvm::APFloat &rhs, QualType type) { + // The Environment ensures we always get a persistent APFloat in + // BasicValueFactory, so we don't need to get the APFloat from + // BasicValueFactory again. + assert(lhs); + assert(!Loc::isLocType(type)); + return nonloc::SymbolVal(SymMgr.getSymFloatExpr(lhs, op, rhs, type)); +} + +NonLoc SValBuilder::makeNonLoc(const llvm::APFloat &lhs, + BinaryOperator::Opcode op, const SymExpr *rhs, + QualType type) { + assert(rhs); + assert(!Loc::isLocType(type)); + return nonloc::SymbolVal(SymMgr.getFloatSymExpr(lhs, op, rhs, type)); +} + +NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, QualType type) { assert(lhs && rhs); assert(!Loc::isLocType(type)); return nonloc::SymbolVal(SymMgr.getSymSymExpr(lhs, op, rhs, type)); } -NonLoc SValBuilder::makeNonLoc(const SymExpr *operand, - QualType fromTy, QualType toTy) { +NonLoc SValBuilder::makeNonLoc(const SymExpr *operand, QualType fromTy, + QualType toTy) { assert(operand); assert(!Loc::isLocType(toTy)); return nonloc::SymbolVal(SymMgr.getCastSymbol(operand, fromTy, toTy)); @@ -82,7 +102,7 @@ // Common case: we have an appropriately sized integer. if (Optional CI = val.getAs()) { - const llvm::APSInt& I = CI->getValue(); + const llvm::APSInt &I = CI->getValue(); if (I.getBitWidth() == ArrayIndexWidth && I.isSigned()) return val; } @@ -90,12 +110,13 @@ return evalCastFromNonLoc(val.castAs(), ArrayIndexTy); } -nonloc::ConcreteInt SValBuilder::makeBoolVal(const CXXBoolLiteralExpr *boolean){ +nonloc::ConcreteInt +SValBuilder::makeBoolVal(const CXXBoolLiteralExpr *boolean) { return makeTruthVal(boolean->getValue()); } DefinedOrUnknownSVal -SValBuilder::getRegionValueSymbolVal(const TypedValueRegion* region) { +SValBuilder::getRegionValueSymbolVal(const TypedValueRegion *region) { QualType T = region->getValueType(); if (T->isNullPtrType()) @@ -149,7 +170,6 @@ return nonloc::SymbolVal(sym); } - DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const Stmt *stmt, const LocationContext *LCtx, QualType type, @@ -168,10 +188,8 @@ return nonloc::SymbolVal(sym); } -DefinedOrUnknownSVal -SValBuilder::getConjuredHeapSymbolVal(const Expr *E, - const LocationContext *LCtx, - unsigned VisitCount) { +DefinedOrUnknownSVal SValBuilder::getConjuredHeapSymbolVal( + const Expr *E, const LocationContext *LCtx, unsigned VisitCount) { QualType T = E->getType(); assert(Loc::isLocType(T)); assert(SymbolManager::canSymbolicate(T)); @@ -200,7 +218,7 @@ DefinedOrUnknownSVal SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, - const TypedValueRegion *region) { + const TypedValueRegion *region) { QualType T = region->getValueType(); if (T->isNullPtrType()) @@ -217,7 +235,7 @@ return nonloc::SymbolVal(sym); } -DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl* DD) { +DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl *DD) { assert(!DD || isa(DD) || isa(DD)); if (auto *MD = dyn_cast_or_null(DD)) { @@ -241,18 +259,18 @@ CanQualType locTy, const LocationContext *locContext, unsigned blockCount) { - const BlockCodeRegion *BC = - MemMgr.getBlockCodeRegion(block, locTy, locContext->getAnalysisDeclContext()); - const BlockDataRegion *BD = MemMgr.getBlockDataRegion(BC, locContext, - blockCount); + const BlockCodeRegion *BC = MemMgr.getBlockCodeRegion( + block, locTy, locContext->getAnalysisDeclContext()); + const BlockDataRegion *BD = + MemMgr.getBlockDataRegion(BC, locContext, blockCount); return loc::MemRegionVal(BD); } /// Return a memory region for the 'this' object reference. loc::MemRegionVal SValBuilder::getCXXThis(const CXXMethodDecl *D, const StackFrameContext *SFC) { - return loc::MemRegionVal(getRegionManager(). - getCXXThisRegion(D->getThisType(getContext()), SFC)); + return loc::MemRegionVal( + getRegionManager().getCXXThisRegion(D->getThisType(getContext()), SFC)); } /// Return a memory region for the 'this' object reference. @@ -304,6 +322,9 @@ case Stmt::IntegerLiteralClass: return makeIntVal(cast(E)); + case Stmt::FloatingLiteralClass: + return makeFloatVal(cast(E)); + case Stmt::ObjCBoolLiteralExprClass: return makeBoolVal(cast(E)); @@ -316,7 +337,8 @@ default: break; case CK_ArrayToPointerDecay: - case CK_BitCast: { + case CK_BitCast: + case CK_IntegralToFloating: { const Expr *SE = CE->getSubExpr(); Optional Val = getConstantVal(SE); if (!Val) @@ -351,31 +373,49 @@ //===----------------------------------------------------------------------===// SVal SValBuilder::makeSymExprValNN(ProgramStateRef State, - BinaryOperator::Opcode Op, - NonLoc LHS, NonLoc RHS, - QualType ResultTy) { + BinaryOperator::Opcode Op, NonLoc LHS, + NonLoc RHS, QualType ResultTy) { + ConstraintManager &CM = getStateManager().getConstraintManager(); + const SymExpr *symLHS = LHS.getAsSymExpr(); const SymExpr *symRHS = RHS.getAsSymExpr(); // TODO: When the Max Complexity is reached, we should conjure a symbol // instead of generating an Unknown value and propagate the taint info to it. const unsigned MaxComp = 10000; // 100000 28X + // If constraint manager doesn't support floating-point expressions, skip + // generating those constraints. if (symLHS && symRHS && - (symLHS->computeComplexity() + symRHS->computeComplexity()) < MaxComp) + (symLHS->computeComplexity() + symRHS->computeComplexity()) < MaxComp) { + if (!CM.canReasonAboutFloats() && (symLHS->getType()->isFloatingType() || + symRHS->getType()->isFloatingType())) + return UnknownVal(); return makeNonLoc(symLHS, Op, symRHS, ResultTy); + } - if (symLHS && symLHS->computeComplexity() < MaxComp) + if (symLHS && symLHS->computeComplexity() < MaxComp) { if (Optional rInt = RHS.getAs()) return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy); + if (Optional rFloat = + RHS.getAs()) { + if (CM.canReasonAboutFloats()) + return makeNonLoc(symLHS, Op, rFloat->getValue(), ResultTy); + } + } - if (symRHS && symRHS->computeComplexity() < MaxComp) + if (symRHS && symRHS->computeComplexity() < MaxComp) { if (Optional lInt = LHS.getAs()) return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy); + if (Optional lFloat = + LHS.getAs()) { + if (CM.canReasonAboutFloats()) + return makeNonLoc(lFloat->getValue(), Op, symRHS, ResultTy); + } + } return UnknownVal(); } - SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type) { @@ -421,7 +461,7 @@ /// and restrict qualifiers. Also, assume that all types are similar to 'void'. /// Assumes the input types are canonical. static bool shouldBeModeledWithNoOp(ASTContext &Context, QualType ToTy, - QualType FromTy) { + QualType FromTy) { while (Context.UnwrapSimilarPointerTypes(ToTy, FromTy)) { Qualifiers Quals1, Quals2; ToTy = Context.getUnqualifiedArrayType(ToTy, Quals1); @@ -518,7 +558,7 @@ // For const casts, casts to void, just propagate the value. if (!castTy->isVariableArrayType() && !originalTy->isVariableArrayType()) if (shouldBeModeledWithNoOp(Context, Context.getPointerType(castTy), - Context.getPointerType(originalTy))) + Context.getPointerType(originalTy))) return val; // Check for casts from pointers to integers. @@ -546,7 +586,7 @@ // Check for casts from array type to another type. if (const ArrayType *arrayT = - dyn_cast(originalTy.getCanonicalType())) { + dyn_cast(originalTy.getCanonicalType())) { // We will always decay to a pointer. QualType elemTy = arrayT->getElementType(); val = StateMgr.ArrayToPointer(val.castAs(), elemTy); @@ -576,14 +616,14 @@ // FIXME: We should handle the case where we strip off view layers to get // to a desugared type. if (!Loc::isLocType(castTy)) { - // 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. + // 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(); } Index: lib/StaticAnalyzer/Core/SVals.cpp =================================================================== --- lib/StaticAnalyzer/Core/SVals.cpp +++ lib/StaticAnalyzer/Core/SVals.cpp @@ -19,6 +19,7 @@ #include "clang/AST/DeclCXX.h" using namespace clang; using namespace ento; +using llvm::APFloat; using llvm::APSInt; //===----------------------------------------------------------------------===// @@ -204,8 +205,16 @@ // Useful predicates. //===----------------------------------------------------------------------===// +bool SVal::isFloat() const { + if (Optional SV = getAs()) { + return SV->getSymbol()->getType()->isRealFloatingType(); + } + return getAs().hasValue(); +} + bool SVal::isConstant() const { - return getAs() || getAs(); + return getAs() || getAs() || + getAs(); } bool SVal::isConstant(int I) const { @@ -216,7 +225,15 @@ return false; } +bool SVal::isConstant(APFloat &V) const { + if (Optional NF = getAs()) + return NF->getValue().compare(V) == llvm::APFloat::cmpEqual; + return false; +} + bool SVal::isZeroConstant() const { + if (Optional NF = getAs()) + return NF->getValue().isPosZero() || NF->getValue().isNegZero(); return isConstant(0); } @@ -247,6 +264,31 @@ return svalBuilder.makeIntVal(-getValue()); } +nonloc::ConcreteFloat +nonloc::ConcreteFloat::evalMinus(SValBuilder &svalBuilder) const { + llvm::APFloat Value = getValue(); + Value.changeSign(); + return svalBuilder.makeFloatVal(Value); +} + +SVal nonloc::ConcreteFloat::evalBinOp(SValBuilder &svalBuilder, + BinaryOperator::Opcode Op, + const nonloc::ConcreteFloat& R) const { + if (BinaryOperator::isComparisonOp(Op)) { + const llvm::APSInt *V = svalBuilder.getBasicValueFactory().evalAPFloatComparison( + Op, getValue(), R.getValue()); + if (V) + return nonloc::ConcreteInt(*V); + return UnknownVal(); + } + + const llvm::APFloat *V = svalBuilder.getBasicValueFactory().evalAPFloat( + Op, getValue(), R.getValue()); + if (V) + return nonloc::ConcreteFloat(*V); + return UnknownVal(); +} + //===----------------------------------------------------------------------===// // Transfer function dispatch for Locs. //===----------------------------------------------------------------------===// @@ -300,6 +342,17 @@ << C.getValue().getBitWidth() << 'b'; break; } + case nonloc::ConcreteFloatKind: { + const nonloc::ConcreteFloat& C = castAs(); + SmallString<24> Chars; + + C.getValue().toString(Chars, 0, 0); + os << Chars + << ' ' + << llvm::APFloat::semanticsSizeInBits(C.getValue().getSemantics()) + << 'b'; + break; + } case nonloc::SymbolValKind: { os << castAs().getSymbol(); break; Index: lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp =================================================================== --- lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp +++ lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -53,9 +53,6 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State, NonLoc Cond, bool Assumption) { - - // We cannot reason about SymSymExprs, and can only reason about some - // SymIntExprs. if (!canReasonAbout(Cond)) { // Just add the constraint to the expression without trying to simplify. SymbolRef Sym = Cond.getAsSymExpr(); @@ -74,6 +71,12 @@ return assumeSym(State, Sym, Assumption); } + case nonloc::ConcreteFloatKind: { + bool b = !Cond.castAs().getValue().isZero(); + bool isFeasible = b ? Assumption : !Assumption; + return isFeasible ? State : nullptr; + } + case nonloc::ConcreteIntKind: { bool b = Cond.castAs().getValue() != 0; bool isFeasible = b ? Assumption : !Assumption; @@ -117,7 +120,31 @@ if (SymbolRef Sym = Value.getAsSymbol()) return assumeSymInclusiveRange(State, Sym, From, To, InRange); return State; - } // end switch + } + + case nonloc::ConcreteFloatKind: { + BasicValueFactory &BVF = getBasicVals(); + + const llvm::APFloat &FloatVal = + Value.castAs().getValue(); + llvm::APFloat FromF(FloatVal.getSemantics(), llvm::APFloat::uninitialized); + llvm::APFloat ToF(FloatVal.getSemantics(), llvm::APFloat::uninitialized); + bool isOk = BVF.Convert(FromF, From); + assert(isOk && "Integer failed to convert to float!"); + (void)isOk; + isOk = BVF.Convert(ToF, To); + assert(isOk && "Integer failed to convert to float!"); + (void)isOk; + llvm::APFloat::cmpResult CompareFrom = FloatVal.compare(FromF); + llvm::APFloat::cmpResult CompareTo = FloatVal.compare(ToF); + + bool IsInRange = (CompareFrom == llvm::APFloat::cmpGreaterThan || + CompareFrom == llvm::APFloat::cmpEqual) && + (CompareTo == llvm::APFloat::cmpLessThan || + CompareTo == llvm::APFloat::cmpEqual); + bool isFeasible = (IsInRange == InRange); + return isFeasible ? State : nullptr; + } case nonloc::ConcreteIntKind: { const llvm::APSInt &IntVal = Value.castAs().getValue(); Index: lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp =================================================================== --- lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -11,9 +11,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" using namespace clang; @@ -29,28 +29,36 @@ public: SimpleSValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context, ProgramStateManager &stateMgr) - : SValBuilder(alloc, context, stateMgr) {} + : SValBuilder(alloc, context, stateMgr) {} ~SimpleSValBuilder() override {} SVal evalMinus(NonLoc val) override; SVal evalComplement(NonLoc val) override; - SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, - NonLoc lhs, NonLoc rhs, QualType resultTy) override; - SVal evalBinOpLL(ProgramStateRef state, BinaryOperator::Opcode op, - Loc lhs, Loc rhs, QualType resultTy) override; - SVal evalBinOpLN(ProgramStateRef state, BinaryOperator::Opcode op, - Loc lhs, NonLoc rhs, QualType resultTy) override; - - /// getKnownValue - evaluates a given SVal. If the SVal has only one possible - /// (integer) value, that value is returned. Otherwise, returns NULL. - const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal V) override; + SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc lhs, + NonLoc rhs, QualType resultTy) override; + SVal evalBinOpLL(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, + Loc rhs, QualType resultTy) override; + SVal evalBinOpLN(ProgramStateRef state, BinaryOperator::Opcode op, Loc lhs, + NonLoc rhs, QualType resultTy) override; + + /// getKnownIntValue - evaluates a given SVal. If the SVal has only one possible + /// integer value, that value is returned. Otherwise, returns NULL. + const llvm::APSInt *getKnownIntValue(ProgramStateRef state, SVal V) override; + + /// Evaluates a given SVal. If the SVal has only one possible float value, + /// that value is returned. Otherwise, returns NULL. + const llvm::APFloat *getKnownFloatValue(ProgramStateRef state, + SVal val) override; /// Recursively descends into symbolic expressions and replaces symbols - /// with their known values (in the sense of the getKnownValue() method). + /// with their known values (in the sense of the getKnownIntValue() method). SVal simplifySVal(ProgramStateRef State, SVal V) override; SVal MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op, const llvm::APSInt &RHS, QualType resultTy); + + SVal MakeSymFloatVal(const SymExpr *LHS, BinaryOperator::Opcode op, + const llvm::APFloat &RHS, QualType resultTy); }; } // end anonymous namespace @@ -67,7 +75,7 @@ SVal SimpleSValBuilder::dispatchCast(SVal Val, QualType CastTy) { assert(Val.getAs() || Val.getAs()); return Val.getAs() ? evalCastFromLoc(Val.castAs(), CastTy) - : evalCastFromNonLoc(Val.castAs(), CastTy); + : evalCastFromNonLoc(Val.castAs(), CastTy); } SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { @@ -94,7 +102,7 @@ // extension/truncation of symbolic integers. This prevents us from losing // precision when we assign 'x = y' and 'y' is symbolic and x and y are // different integer types. - if (haveSameType(T, castTy)) + if (haveSameType(T, castTy)) return val; if (!isLocType) @@ -102,28 +110,79 @@ return UnknownVal(); } - // If value is a non-integer constant, produce unknown. - if (!val.getAs()) + // If value is an unsupported constant, produce unknown. + if (!val.getAs() && !val.getAs()) return UnknownVal(); // Handle casts to a boolean type. if (castTy->isBooleanType()) { - bool b = val.castAs().getValue().getBoolValue(); - return makeTruthVal(b, castTy); + if (Optional CI = val.getAs()) + return makeTruthVal(CI->getValue().getBoolValue(), castTy); + if (Optional CF = val.getAs()) + return makeTruthVal(!CF->getValue().isZero(), castTy); } - // Only handle casts from integers to integers - if val is an integer constant - // being cast to a non-integer type, produce unknown. - if (!isLocType && !castTy->isIntegralOrEnumerationType()) - return UnknownVal(); + // Handle casts from integer + if (Optional CI = val.getAs()) { + if (castTy->isRealFloatingType()) { + if (isLocType) + return UnknownVal(); - llvm::APSInt i = val.castAs().getValue(); - BasicVals.getAPSIntType(castTy).apply(i); + llvm::APFloat Value(Context.getFloatTypeSemantics(castTy)); + // Cannot be represented in destination type, this is undefined behavior + // TODO: Emit warning for this + if (!BasicValueFactory::Convert(Value, CI->getValue())) + return UndefinedVal(); + return makeFloatVal(Value); + } - if (isLocType) - return makeIntLocVal(i); - else - return makeIntVal(i); + // Only handle casts from integers to integers - if val is an integer + // constant being cast to a non-integer type, produce unknown. + if (!isLocType && !castTy->isIntegralOrEnumerationType()) + return UnknownVal(); + + llvm::APSInt Value = CI->getValue(); + BasicVals.getAPSIntType(castTy).apply(Value); + if (isLocType) + return makeIntLocVal(Value); + return makeIntVal(Value); + } + + // Handle casts from real floating-point + if (Optional CF = val.getAs()) { + bool lossOfPrecision; + + if (castTy->isIntegralOrEnumerationType()) { + llvm::APSInt Value(Context.getTypeSize(castTy), + !castTy->isSignedIntegerOrEnumerationType()); + // Cannot be represented in destination type, this is undefined behavior + // TODO: Emit warning for this + if (!BasicValueFactory::Convert(Value, CF->getValue())) + return UndefinedVal(); + if (isLocType) + return makeIntLocVal(Value); + return makeIntVal(Value); + } + + if (castTy->isRealFloatingType()) { + if (isLocType) + return UnknownVal(); + + llvm::APFloat Value = CF->getValue(); + llvm::APFloat::opStatus Status = + Value.convert(Context.getFloatTypeSemantics(castTy), + llvm::APFloat::rmNearestTiesToEven, &lossOfPrecision); + // Cannot be represented in destination type, this is undefined behavior + // TODO: Emit warning for this + if (Status & (llvm::APFloat::opOverflow | llvm::APFloat::opInvalidOp)) + return UndefinedVal(); + return makeFloatVal(Value); + } + + return UnknownVal(); + } + + return UnknownVal(); } SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) { @@ -146,26 +205,26 @@ // unless this is a weak function or a symbolic region. if (castTy->isBooleanType()) { switch (val.getSubKind()) { - case loc::MemRegionValKind: { - const MemRegion *R = val.castAs().getRegion(); - if (const FunctionCodeRegion *FTR = dyn_cast(R)) - if (const FunctionDecl *FD = dyn_cast(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()) - return nonloc::SymbolVal(SymR->getSymbol()); - - // FALL-THROUGH - LLVM_FALLTHROUGH; - } + case loc::MemRegionValKind: { + const MemRegion *R = val.castAs().getRegion(); + if (const FunctionCodeRegion *FTR = dyn_cast(R)) + if (const FunctionDecl *FD = dyn_cast(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()) + return nonloc::SymbolVal(SymR->getSymbol()); + + // FALL-THROUGH + LLVM_FALLTHROUGH; + } - case loc::GotoLabelKind: - // Labels and non-symbolic memory regions are always true. - return makeTruthVal(true, castTy); + case loc::GotoLabelKind: + // Labels and non-symbolic memory regions are always true. + return makeTruthVal(true, castTy); } } @@ -194,6 +253,8 @@ switch (val.getSubKind()) { case nonloc::ConcreteIntKind: return val.castAs().evalMinus(*this); + case nonloc::ConcreteFloatKind: + return val.castAs().evalMinus(*this); default: return UnknownVal(); } @@ -213,9 +274,9 @@ //===----------------------------------------------------------------------===// SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS, - BinaryOperator::Opcode op, - const llvm::APSInt &RHS, - QualType resultTy) { + BinaryOperator::Opcode op, + const llvm::APSInt &RHS, + QualType resultTy) { bool isIdempotent = false; // Check for a few special cases with known reductions first. @@ -277,7 +338,7 @@ // Wrap the LHS up in a NonLoc again and let evalCastFromNonLoc do the // dirty work. if (isIdempotent) - return evalCastFromNonLoc(nonloc::SymbolVal(LHS), resultTy); + return evalCastFromNonLoc(nonloc::SymbolVal(LHS), resultTy); // If we reach this point, the expression cannot be simplified. // Make a SymbolVal for the entire expression, after converting the RHS. @@ -307,34 +368,49 @@ return makeNonLoc(LHS, op, *ConvertedRHS, resultTy); } +SVal SimpleSValBuilder::MakeSymFloatVal(const SymExpr *LHS, + BinaryOperator::Opcode op, + const llvm::APFloat &RHS, + QualType resultTy) { + ConstraintManager &CM = getStateManager().getConstraintManager(); + if (!CM.canReasonAboutFloats()) + return UnknownVal(); + + if (RHS.isNaN()) + return makeTruthVal(op == BO_NE, resultTy); + + const llvm::APFloat *ConvertedRHS = &RHS; + return makeNonLoc(LHS, op, *ConvertedRHS, resultTy); +} + SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, - BinaryOperator::Opcode op, - NonLoc lhs, NonLoc rhs, - QualType resultTy) { + BinaryOperator::Opcode op, NonLoc lhs, + NonLoc rhs, QualType resultTy) { NonLoc InputLHS = lhs; NonLoc InputRHS = rhs; // Handle trivial case where left-side and right-side are the same. - if (lhs == rhs) + // Skip floating-point operations, which are not commutative. + if (lhs == rhs && !lhs.isFloat()) switch (op) { - default: - break; - case BO_EQ: - case BO_LE: - case BO_GE: - return makeTruthVal(true, resultTy); - case BO_LT: - case BO_GT: - case BO_NE: - return makeTruthVal(false, resultTy); - case BO_Xor: - case BO_Sub: - if (resultTy->isIntegralOrEnumerationType()) - return makeIntVal(0, resultTy); - return evalCastFromNonLoc(makeIntVal(0, /*Unsigned=*/false), resultTy); - case BO_Or: - case BO_And: - return evalCastFromNonLoc(lhs, resultTy); + default: + break; + case BO_EQ: + case BO_LE: + case BO_GE: + return makeTruthVal(true, resultTy); + case BO_LT: + case BO_GT: + case BO_NE: + return makeTruthVal(false, resultTy); + case BO_Xor: + case BO_Sub: + if (resultTy->isIntegralOrEnumerationType()) + return makeIntVal(0, resultTy); + return evalCastFromNonLoc(makeIntVal(0, /*Unsigned=*/false), resultTy); + case BO_Or: + case BO_And: + return evalCastFromNonLoc(lhs, resultTy); } while (1) { @@ -348,54 +424,54 @@ RPTM = rhs.castAs(); auto LPTMD = LPTM.getPTMData(), RPTMD = RPTM.getPTMData(); switch (op) { - case BO_EQ: - return makeTruthVal(LPTMD == RPTMD, resultTy); - case BO_NE: - return makeTruthVal(LPTMD != RPTMD, resultTy); - default: - return UnknownVal(); + case BO_EQ: + return makeTruthVal(LPTMD == RPTMD, resultTy); + case BO_NE: + return makeTruthVal(LPTMD != RPTMD, resultTy); + default: + return UnknownVal(); } } case nonloc::LocAsIntegerKind: { Loc lhsL = lhs.castAs().getLoc(); switch (rhs.getSubKind()) { - case nonloc::LocAsIntegerKind: - return evalBinOpLL(state, op, lhsL, - rhs.castAs().getLoc(), - resultTy); - case nonloc::ConcreteIntKind: { - // Transform the integer into a location and compare. - // FIXME: This only makes sense for comparisons. If we want to, say, - // add 1 to a LocAsInteger, we'd better unpack the Loc and add to it, - // then pack it back into a LocAsInteger. - llvm::APSInt i = rhs.castAs().getValue(); - BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i); - return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy); - } + case nonloc::LocAsIntegerKind: + return evalBinOpLL(state, op, lhsL, + rhs.castAs().getLoc(), + resultTy); + case nonloc::ConcreteIntKind: { + // Transform the integer into a location and compare. + // FIXME: This only makes sense for comparisons. If we want to, say, + // add 1 to a LocAsInteger, we'd better unpack the Loc and add to it, + // then pack it back into a LocAsInteger. + llvm::APSInt i = rhs.castAs().getValue(); + BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i); + return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy); + } + default: + switch (op) { + case BO_EQ: + return makeTruthVal(false, resultTy); + case BO_NE: + return makeTruthVal(true, resultTy); default: - switch (op) { - case BO_EQ: - return makeTruthVal(false, resultTy); - case BO_NE: - return makeTruthVal(true, resultTy); - default: - // This case also handles pointer arithmetic. - return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); - } + // This case also handles pointer arithmetic. + return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); + } } } case nonloc::ConcreteIntKind: { llvm::APSInt LHSValue = lhs.castAs().getValue(); // If we're dealing with two known constants, just perform the operation. - if (const llvm::APSInt *KnownRHSValue = getKnownValue(state, rhs)) { + if (const llvm::APSInt *KnownRHSValue = getKnownIntValue(state, rhs)) { llvm::APSInt RHSValue = *KnownRHSValue; if (BinaryOperator::isComparisonOp(op)) { // We're looking for a type big enough to compare the two values. // FIXME: This is not correct. char + short will result in a promotion // to int. Unfortunately we have lost types by this point. - APSIntType CompareType = std::max(APSIntType(LHSValue), - APSIntType(RHSValue)); + APSIntType CompareType = + std::max(APSIntType(LHSValue), APSIntType(RHSValue)); CompareType.apply(LHSValue); CompareType.apply(RHSValue); } else if (!BinaryOperator::isShiftOp(op)) { @@ -405,7 +481,7 @@ } const llvm::APSInt *Result = - BasicVals.evalAPSInt(op, LHSValue, RHSValue); + BasicVals.evalAPSInt(op, LHSValue, RHSValue); if (!Result) return UndefinedVal(); @@ -446,6 +522,29 @@ return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); } } + case nonloc::ConcreteFloatKind: { + llvm::APFloat LHSValue = lhs.castAs().getValue(); + + // If we're dealing with two known constants, just perform the operation. + if (const llvm::APFloat *KnownRHSValue = getKnownFloatValue(state, rhs)) { + llvm::APFloat RHSValue = *KnownRHSValue; + + if (BinaryOperator::isComparisonOp(op)) { + const llvm::APSInt *V = + BasicVals.evalAPFloatComparison(op, LHSValue, RHSValue); + if (V) + return nonloc::ConcreteInt(*V); + return UnknownVal(); + } + + const llvm::APFloat *V = BasicVals.evalAPFloat(op, LHSValue, RHSValue); + if (V) + return nonloc::ConcreteFloat(*V); + return UnknownVal(); + } + + return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); + } case nonloc::SymbolValKind: { // We only handle LHS as simple symbols or SymIntExprs. SymbolRef Sym = lhs.castAs().getSymbol(); @@ -488,20 +587,19 @@ case BO_GE: case BO_EQ: case BO_NE: - assert(resultTy->isBooleanType() || - resultTy == getConditionType()); + assert(resultTy->isBooleanType() || resultTy == getConditionType()); assert(symIntExpr->getType()->isBooleanType() || getContext().hasSameUnqualifiedType(symIntExpr->getType(), getConditionType())); // Negate the comparison and make a value. opc = BinaryOperator::negateComparisonOp(opc); - return makeNonLoc(symIntExpr->getLHS(), opc, - symIntExpr->getRHS(), resultTy); + return makeNonLoc(symIntExpr->getLHS(), opc, symIntExpr->getRHS(), + resultTy); } } // For now, only handle expressions whose RHS is a constant. - if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) { + if (const llvm::APSInt *RHSValue = getKnownIntValue(state, rhs)) { // If both the LHS and the current expression are additive, // fold their constants and try again. if (BinaryOperator::isAdditiveOp(op)) { @@ -548,8 +646,13 @@ } // Is the RHS a constant? - if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) - return MakeSymIntVal(Sym, op, *RHSValue, resultTy); + if (rhs.isFloat()) { + if (const llvm::APFloat *RHSValue = getKnownFloatValue(state, rhs)) + return MakeSymFloatVal(Sym, op, *RHSValue, resultTy); + } else { + if (const llvm::APSInt *RHSValue = getKnownIntValue(state, rhs)) + return MakeSymIntVal(Sym, op, *RHSValue, resultTy); + } // Give up -- this is not a symbolic expression we can handle. return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy); @@ -606,9 +709,8 @@ // FIXME: all this logic will change if/when we have MemRegion::getLocation(). SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, - BinaryOperator::Opcode op, - Loc lhs, Loc rhs, - QualType resultTy) { + BinaryOperator::Opcode op, Loc lhs, Loc rhs, + QualType resultTy) { // Only comparisons and subtractions are valid operations on two pointers. // See [C99 6.5.5 through 6.5.14] or [C++0x 5.6 through 5.15]. // However, if a pointer is casted to an integer, evalBinOpNN may end up @@ -790,7 +892,7 @@ // relying on us. if (LeftBase != RightBase && ((!isa(LeftBase) && !isa(RightBase)) || - (isa(LeftMS) || isa(RightMS))) ){ + (isa(LeftMS) || isa(RightMS)))) { switch (op) { default: return UnknownVal(); @@ -842,8 +944,8 @@ const FieldRegion *RightFR = dyn_cast(RightMR); const FieldRegion *LeftFR = dyn_cast(LeftMR); if (RightFR && LeftFR) { - SVal R = evalBinOpFieldRegionFieldRegion(LeftFR, RightFR, op, resultTy, - *this); + SVal R = + evalBinOpFieldRegionFieldRegion(LeftFR, RightFR, op, resultTy, *this); if (!R.isUnknown()) return R; } @@ -859,20 +961,20 @@ int64_t right = RightOffset.getOffset(); switch (op) { - default: - return UnknownVal(); - case BO_LT: - return makeTruthVal(left < right, resultTy); - case BO_GT: - return makeTruthVal(left > right, resultTy); - case BO_LE: - return makeTruthVal(left <= right, resultTy); - case BO_GE: - return makeTruthVal(left >= right, resultTy); - case BO_EQ: - return makeTruthVal(left == right, resultTy); - case BO_NE: - return makeTruthVal(left != right, resultTy); + default: + return UnknownVal(); + case BO_LT: + return makeTruthVal(left < right, resultTy); + case BO_GT: + return makeTruthVal(left > right, resultTy); + case BO_LE: + return makeTruthVal(left <= right, resultTy); + case BO_GE: + return makeTruthVal(left >= right, resultTy); + case BO_EQ: + return makeTruthVal(left == right, resultTy); + case BO_NE: + return makeTruthVal(left != right, resultTy); } } @@ -890,8 +992,8 @@ } SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, - BinaryOperator::Opcode op, - Loc lhs, NonLoc rhs, QualType resultTy) { + BinaryOperator::Opcode op, Loc lhs, + NonLoc rhs, QualType resultTy) { if (op >= BO_PtrMemD && op <= BO_PtrMemI) { if (auto PTMSV = rhs.getAs()) { if (PTMSV->isNullMemberPointer()) @@ -901,7 +1003,7 @@ for (const auto &I : *PTMSV) Result = StateMgr.getStoreManager().evalDerivedToBase( - Result, I->getType(),I->isVirtual()); + Result, I->getType(), I->isVirtual()); return state->getLValue(FD, Result); } } @@ -935,14 +1037,14 @@ // Compute the adjusted pointer. switch (op) { - case BO_Add: - rightI = leftI + rightI; - break; - case BO_Sub: - rightI = leftI - rightI; - break; - default: - llvm_unreachable("Invalid pointer arithmetic operation"); + case BO_Add: + rightI = leftI + rightI; + break; + case BO_Sub: + rightI = leftI - rightI; + break; + default: + llvm_unreachable("Invalid pointer arithmetic operation"); } return loc::ConcreteInt(getBasicValueFactory().getValue(rightI)); } @@ -959,12 +1061,11 @@ if (const ElementRegion *elemReg = dyn_cast(region)) { assert(op == BO_Add || op == BO_Sub); - index = evalBinOpNN(state, op, elemReg->getIndex(), rhs, - getArrayIndexType()); + index = + evalBinOpNN(state, op, elemReg->getIndex(), rhs, getArrayIndexType()); superR = cast(elemReg->getSuperRegion()); elementType = elemReg->getElementType(); - } - else if (isa(region)) { + } else if (isa(region)) { assert(op == BO_Add || op == BO_Sub); index = (op == BO_Add) ? rhs : evalMinus(rhs); superR = cast(region); @@ -977,18 +1078,25 @@ } if (Optional indexV = index.getAs()) { - return loc::MemRegionVal(MemMgr.getElementRegion(elementType, *indexV, - superR, getContext())); + return loc::MemRegionVal( + MemMgr.getElementRegion(elementType, *indexV, superR, getContext())); } } return UnknownVal(); } -const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state, - SVal V) { +const llvm::APSInt *SimpleSValBuilder::getKnownIntValue(ProgramStateRef state, + SVal V) { if (V.isUnknownOrUndef()) return nullptr; + if (V.isFloat()) { +#ifndef NDEBUG + assert(false && "Requesting integer value of floating-point type!"); +#endif + return nullptr; + } + if (Optional X = V.getAs()) return &X->getValue(); @@ -996,13 +1104,38 @@ return &X->getValue(); if (SymbolRef Sym = V.getAsSymbol()) - return state->getConstraintManager().getSymVal(state, Sym); + return state->getConstraintManager().getSymIntVal(state, Sym); // FIXME: Add support for SymExprs in RangeConstraintManager. return nullptr; } +const llvm::APFloat * +SimpleSValBuilder::getKnownFloatValue(ProgramStateRef state, SVal V) { + if (V.isUnknownOrUndef()) + return nullptr; + + if (!V.isFloat()) { +#ifndef NDEBUG + assert(false && "Requesting floating-point value of integer type!"); +#endif + return nullptr; + } + + if (Optional X = V.getAs()) + return &X->getValue(); + + if (SymbolRef Sym = V.getAsSymbol()) + return state->getConstraintManager().getSymFloatVal(state, Sym); + + if (Optional NV = V.getAs()) + if (SymbolRef Sym = NV->getAsSymExpr()) + return state->getConstraintManager().getSymFloatVal(state, Sym); + + return nullptr; +} + SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) { // For now, this function tries to constant-fold symbols inside a // nonloc::SymbolVal, and does nothing else. More simplifications should @@ -1017,10 +1150,18 @@ : State(State), SVB(State->getStateManager().getSValBuilder()) {} SVal VisitSymbolData(const SymbolData *S) { - if (const llvm::APSInt *I = - SVB.getKnownValue(State, nonloc::SymbolVal(S))) - return Loc::isLocType(S->getType()) ? (SVal)SVB.makeIntLocVal(*I) - : (SVal)SVB.makeIntVal(*I); + if (!S->getType()->isRealFloatingType()) { + const llvm::APSInt *I = SVB.getKnownIntValue(State, nonloc::SymbolVal(S)); + if (I) + return Loc::isLocType(S->getType()) ? (SVal)SVB.makeIntLocVal(*I) + : (SVal)SVB.makeIntVal(*I); + } else { + const llvm::APFloat *F = + SVB.getKnownFloatValue(State, nonloc::SymbolVal(S)); + assert(!Loc::isLocType(S->getType())); + if (F) + return (SVal)SVB.makeFloatVal(*F); + } return nonloc::SymbolVal(S); } @@ -1053,6 +1194,18 @@ return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()); } + SVal VisitSymFloatExpr(const SymFloatExpr *S) { + SVal LHS = Visit(S->getLHS()); + SVal RHS = SVB.makeFloatVal(S->getRHS()); + return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()); + } + + SVal VisitFloatSymExpr(const FloatSymExpr *S) { + SVal RHS = Visit(S->getRHS()); + SVal LHS = SVB.makeFloatVal(S->getLHS()); + return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()); + } + SVal VisitSymbolCast(const SymbolCast *S) { SVal V = Visit(S->getOperand()); return SVB.evalCast(V, S->getType(), S->getOperand()->getType()); Index: lib/StaticAnalyzer/Core/SymbolManager.cpp =================================================================== --- lib/StaticAnalyzer/Core/SymbolManager.cpp +++ lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -48,6 +48,29 @@ os << ')'; } +void SymFloatExpr::dumpToStream(raw_ostream &os) const { + SmallString<24> Chars; + getRHS().toString(Chars, 0, 0); + + os << '('; + getLHS()->dumpToStream(os); + os << ") " + << BinaryOperator::getOpcodeStr(getOpcode()) << ' ' + << Chars; +} + +void FloatSymExpr::dumpToStream(raw_ostream &os) const { + SmallString<24> Chars; + getLHS().toString(Chars, 0, 0); + + os << Chars + << ' ' + << BinaryOperator::getOpcodeStr(getOpcode()) + << " ("; + getRHS()->dumpToStream(os); + os << ')'; +} + void SymSymExpr::dumpToStream(raw_ostream &os) const { os << '('; getLHS()->dumpToStream(os); @@ -131,6 +154,12 @@ case SymExpr::IntSymExprKind: itr.push_back(cast(SE)->getRHS()); return; + case SymExpr::SymFloatExprKind: + itr.push_back(cast(SE)->getLHS()); + return; + case SymExpr::FloatSymExprKind: + itr.push_back(cast(SE)->getRHS()); + return; case SymExpr::SymSymExprKind: { const SymSymExpr *x = cast(SE); itr.push_back(x->getLHS()); @@ -288,6 +317,42 @@ return cast(data); } +const SymFloatExpr *SymbolManager::getSymFloatExpr(const SymExpr *lhs, + BinaryOperator::Opcode op, + const llvm::APFloat& v, + QualType t) { + llvm::FoldingSetNodeID ID; + SymFloatExpr::Profile(ID, lhs, op, v, t); + void *InsertPos; + SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!data) { + data = (SymFloatExpr*) BPAlloc.Allocate(); + new (data) SymFloatExpr(lhs, op, v, t); + DataSet.InsertNode(data, InsertPos); + } + + return cast(data); +} + +const FloatSymExpr *SymbolManager::getFloatSymExpr(const llvm::APFloat& lhs, + BinaryOperator::Opcode op, + const SymExpr *rhs, + QualType t) { + llvm::FoldingSetNodeID ID; + FloatSymExpr::Profile(ID, lhs, op, rhs, t); + void *InsertPos; + SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!data) { + data = (FloatSymExpr*) BPAlloc.Allocate(); + new (data) FloatSymExpr(lhs, op, rhs, t); + DataSet.InsertNode(data, InsertPos); + } + + return cast(data); +} + const SymSymExpr *SymbolManager::getSymSymExpr(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, @@ -340,6 +405,9 @@ if (T->isIntegralOrEnumerationType()) return true; + if (T->isRealFloatingType()) + return true; + if (T->isRecordType() && !T->isUnionType()) return true; @@ -484,6 +552,12 @@ case SymExpr::IntSymExprKind: KnownLive = isLive(cast(sym)->getRHS()); break; + case SymExpr::SymFloatExprKind: + KnownLive = isLive(cast(sym)->getLHS()); + break; + case SymExpr::FloatSymExprKind: + KnownLive = isLive(cast(sym)->getRHS()); + break; case SymExpr::SymSymExprKind: KnownLive = isLive(cast(sym)->getLHS()) && isLive(cast(sym)->getRHS()); Index: lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp =================================================================== --- lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp +++ lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp @@ -904,10 +904,15 @@ bool canReasonAbout(SVal X) const override; + bool canReasonAboutFloats() const override; + ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override; - const llvm::APSInt *getSymVal(ProgramStateRef State, - SymbolRef Sym) const override; + const llvm::APSInt *getSymIntVal(ProgramStateRef State, + SymbolRef Sym) const override; + + const llvm::APFloat *getSymFloatVal(ProgramStateRef State, + SymbolRef Sym) const override; ProgramStateRef removeDeadBindings(ProgramStateRef St, SymbolReaper &SymReaper) override; @@ -983,6 +988,10 @@ // Helper functions. //===------------------------------------------------------------------===// + // Recover the QualType of an APFloat. + // TODO: Refactor to put elsewhere + QualType getAPFloatType(const llvm::APFloat &Float) const; + // Recover the QualType of an APSInt. // TODO: Refactor to put elsewhere QualType getAPSIntType(const llvm::APSInt &Int) const; @@ -1012,6 +1021,11 @@ void doFloatTypeConversion(T &LHS, QualType <y, T &RHS, QualType &RTy) const; + // Callback function for doCast parameter on APFloat type. + static llvm::APFloat castAPFloat(const llvm::APFloat &V, QualType ToTy, + uint64_t ToWidth, QualType FromTy, + uint64_t FromWidth); + // Callback function for doCast parameter on APSInt type. static llvm::APSInt castAPSInt(const llvm::APSInt &V, QualType ToTy, uint64_t ToWidth, QualType FromTy, @@ -1103,6 +1117,10 @@ Sym = SIE->getLHS(); } else if (const IntSymExpr *ISE = dyn_cast(BSE)) { Sym = ISE->getRHS(); + } else if (const SymFloatExpr *SFE = dyn_cast(BSE)) { + Sym = SFE->getLHS(); + } else if (const FloatSymExpr *FSE = dyn_cast(BSE)) { + Sym = FSE->getRHS(); } else if (const SymSymExpr *SSM = dyn_cast(BSE)) { return canReasonAbout(nonloc::SymbolVal(SSM->getLHS())) && canReasonAbout(nonloc::SymbolVal(SSM->getRHS())); @@ -1117,6 +1135,8 @@ return true; } +bool Z3ConstraintManager::canReasonAboutFloats() const { return true; } + ConditionTruthVal Z3ConstraintManager::checkNull(ProgramStateRef State, SymbolRef Sym) { QualType RetTy; @@ -1148,8 +1168,10 @@ return ConditionTruthVal(); } -const llvm::APSInt *Z3ConstraintManager::getSymVal(ProgramStateRef State, - SymbolRef Sym) const { +// TODO: Merge implementation of getSymIntVal() and getSymFloatVal() into one +// templated function, to avoid weird corner cases when casting back and forth +const llvm::APSInt *Z3ConstraintManager::getSymIntVal(ProgramStateRef State, + SymbolRef Sym) const { BasicValueFactory &BVF = getBasicVals(); ASTContext &Ctx = BVF.getContext(); @@ -1193,22 +1215,59 @@ if (CastTy->isVoidType()) return nullptr; - const llvm::APSInt *Value; - if (!(Value = getSymVal(State, CastSym))) - return nullptr; - return &BVF.Convert(SC->getType(), *Value); + // Call the other getSym*Val() function depending on expression type + if (CastSym->getType()->isRealFloatingType()) { + const llvm::APFloat *Float = getSymFloatVal(State, CastSym); + assert(!CastTy->isRealFloatingType()); + llvm::APSInt Int(Ctx.getTypeSize(CastTy), + !CastTy->isSignedIntegerOrEnumerationType()); + if (!Float || !BVF.Convert(Int, *Float)) + return nullptr; + return &BVF.getValue(Int); + } else { + const llvm::APSInt *Value; + if (!(Value = getSymIntVal(State, CastSym))) + return nullptr; + return &BVF.Convert(CastTy, *Value); + } } else if (const BinarySymExpr *BSE = dyn_cast(Sym)) { + // Floating-point comparisons produce an integral result + if (BinaryOperator::isComparisonOp(BSE->getOpcode()) && + (isa(BSE) || isa(BSE) || + (isa(BSE) && + (cast(BSE)->getLHS()->getType()->isFloatingType() || + cast(BSE)->getRHS()->getType()->isFloatingType())))) { + const llvm::APFloat *LHS = nullptr, *RHS = nullptr; + if (const FloatSymExpr *FSE = dyn_cast(BSE)) { + LHS = &FSE->getLHS(); + RHS = getSymFloatVal(State, FSE->getRHS()); + } else if (const SymFloatExpr *SFE = dyn_cast(BSE)) { + LHS = getSymFloatVal(State, SFE->getLHS()); + RHS = &SFE->getRHS(); + } + + if (!LHS || !RHS) + return nullptr; + + llvm::APFloat ConvertedLHS = *LHS, ConvertedRHS = *RHS; + QualType LTy = getAPFloatType(*LHS), RTy = getAPFloatType(*RHS); + doFloatTypeConversion( + ConvertedLHS, LTy, ConvertedRHS, RTy); + return BVF.evalAPFloatComparison(BSE->getOpcode(), ConvertedLHS, + ConvertedRHS); + } + const llvm::APSInt *LHS, *RHS; if (const SymIntExpr *SIE = dyn_cast(BSE)) { - LHS = getSymVal(State, SIE->getLHS()); + LHS = getSymIntVal(State, SIE->getLHS()); RHS = &SIE->getRHS(); } else if (const IntSymExpr *ISE = dyn_cast(BSE)) { LHS = &ISE->getLHS(); - RHS = getSymVal(State, ISE->getRHS()); + RHS = getSymIntVal(State, ISE->getRHS()); } else if (const SymSymExpr *SSM = dyn_cast(BSE)) { // Early termination to avoid expensive call - LHS = getSymVal(State, SSM->getLHS()); - RHS = LHS ? getSymVal(State, SSM->getRHS()) : nullptr; + LHS = getSymIntVal(State, SSM->getLHS()); + RHS = LHS ? getSymIntVal(State, SSM->getRHS()) : nullptr; } else { llvm_unreachable("Unsupported binary expression to get symbol value!"); } @@ -1226,6 +1285,92 @@ llvm_unreachable("Unsupported expression to get symbol value!"); } +const llvm::APFloat *Z3ConstraintManager::getSymFloatVal(ProgramStateRef State, + SymbolRef Sym) const { + BasicValueFactory &BVF = getBasicVals(); + ASTContext &Ctx = BVF.getContext(); + + if (const SymbolData *SD = dyn_cast(Sym)) { + QualType Ty = Sym->getType(); + assert(Ty->isRealFloatingType()); + llvm::APFloat Value(Ctx.getFloatTypeSemantics(Ty)); + + Z3Expr Exp = getZ3DataExpr(SD->getSymbolID(), Ty); + + Solver.reset(); + Solver.addStateConstraints(State); + + // Constraints are unsatisfiable + if (Solver.check() != Z3_L_TRUE) + return nullptr; + + Z3Model Model = Solver.getModel(); + // Model does not assign interpretation + if (!Model.getInterpretation(Exp, Value)) + return nullptr; + + // A value has been obtained, check if it is the only value + Z3Expr NotExp = + Z3Expr::fromFloatBinOp(Exp, BO_NE, Z3Expr::fromAPFloat(Value)); + + Solver.addConstraint(NotExp); + if (Solver.check() == Z3_L_TRUE) + return nullptr; + + // This is the only solution, store it + return &BVF.getValue(Value); + } else if (const SymbolCast *SC = dyn_cast(Sym)) { + SymbolRef CastSym = SC->getOperand(); + QualType CastTy = SC->getType(); + // Skip the void type + if (CastTy->isVoidType()) + return nullptr; + + // Call the other getSym*Val() function depending on expression type + if (!CastSym->getType()->isRealFloatingType()) { + const llvm::APSInt *Int = getSymIntVal(State, CastSym); + assert(CastTy->isRealFloatingType()); + llvm::APFloat Float(Ctx.getFloatTypeSemantics(CastTy)); + if (!Int || !BVF.Convert(Float, *Int)) + return nullptr; + return &BVF.getValue(Float); + } else { + const llvm::APFloat *Value; + if (!(Value = getSymFloatVal(State, CastSym))) + return nullptr; + return &BVF.Convert(CastTy, *Value); + } + } else if (const BinarySymExpr *BSE = dyn_cast(Sym)) { + const llvm::APFloat *LHS, *RHS; + if (const SymFloatExpr *SIE = dyn_cast(BSE)) { + LHS = getSymFloatVal(State, SIE->getLHS()); + RHS = &SIE->getRHS(); + } else if (const FloatSymExpr *ISE = dyn_cast(BSE)) { + LHS = &ISE->getLHS(); + RHS = getSymFloatVal(State, ISE->getRHS()); + } else if (const SymSymExpr *SSM = dyn_cast(BSE)) { + // Early termination to avoid expensive call + LHS = getSymFloatVal(State, SSM->getLHS()); + RHS = LHS ? getSymFloatVal(State, SSM->getRHS()) : nullptr; + } else { + llvm_unreachable("Unsupported binary expression to get symbol value!"); + } + + if (!LHS || !RHS) + return nullptr; + + llvm::APFloat ConvertedLHS = *LHS, ConvertedRHS = *RHS; + QualType LTy = getAPFloatType(*LHS), RTy = getAPFloatType(*RHS); + doFloatTypeConversion( + ConvertedLHS, LTy, ConvertedRHS, RTy); + return BVF.evalAPFloat(BSE->getOpcode(), ConvertedLHS, ConvertedRHS); + } else { + llvm_unreachable("Unsupported expression to get symbol value!"); + } + + return nullptr; +} + ProgramStateRef Z3ConstraintManager::removeDeadBindings(ProgramStateRef State, SymbolReaper &SymReaper) { @@ -1356,6 +1501,16 @@ Z3Expr LHS = Z3Expr::fromAPSInt(fixAPSInt(ISE->getLHS(), NewLInt, <y)); Z3Expr RHS = getZ3SymExpr(ISE->getRHS(), &RTy, hasComparison); return getZ3BinExpr(LHS, LTy, Op, RHS, RTy, RetTy); + } else if (const SymFloatExpr *SFE = dyn_cast(BSE)) { + RTy = getAPFloatType(SFE->getRHS()); + Z3Expr LHS = getZ3SymExpr(SFE->getLHS(), <y, hasComparison); + Z3Expr RHS = Z3Expr::fromAPFloat(SFE->getRHS()); + return getZ3BinExpr(LHS, LTy, Op, RHS, RTy, RetTy); + } else if (const FloatSymExpr *FSE = dyn_cast(BSE)) { + LTy = getAPFloatType(FSE->getLHS()); + Z3Expr LHS = Z3Expr::fromAPFloat(FSE->getLHS()); + Z3Expr RHS = getZ3SymExpr(FSE->getRHS(), &RTy, hasComparison); + return getZ3BinExpr(LHS, LTy, Op, RHS, RTy, RetTy); } else if (const SymSymExpr *SSM = dyn_cast(BSE)) { Z3Expr LHS = getZ3SymExpr(SSM->getLHS(), <y, hasComparison); Z3Expr RHS = getZ3SymExpr(SSM->getRHS(), &RTy, hasComparison); @@ -1402,6 +1557,12 @@ // Helper functions. //===------------------------------------------------------------------===// +QualType Z3ConstraintManager::getAPFloatType(const llvm::APFloat &Float) const { + ASTContext &Ctx = getBasicVals().getContext(); + return Ctx.getRealTypeForBitwidth( + llvm::APFloat::getSizeInBits(Float.getSemantics())); +} + QualType Z3ConstraintManager::getAPSIntType(const llvm::APSInt &Int) const { ASTContext &Ctx = getBasicVals().getContext(); return Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned()); @@ -1595,16 +1756,31 @@ // Note: Safe to skip updating bitwidth because this must terminate int order = Ctx.getFloatingTypeOrder(LTy, RTy); if (order > 0) { - RHS = Z3Expr::fromCast(RHS, LTy, LBitWidth, RTy, RBitWidth); + RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth); RTy = LTy; } else if (order == 0) { - LHS = Z3Expr::fromCast(LHS, RTy, RBitWidth, LTy, LBitWidth); + LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth); LTy = RTy; } else { llvm_unreachable("Unsupported floating-point type cast!"); } } +llvm::APFloat Z3ConstraintManager::castAPFloat(const llvm::APFloat &V, + QualType ToTy, uint64_t ToWidth, + QualType FromTy, + uint64_t FromWidth) { + bool lossOfPrecision; + llvm::APFloat To = V; + bool isOk = + !(To.convert(Z3Expr::getFloatSemantics(ToWidth), + llvm::APFloat::rmNearestTiesToEven, &lossOfPrecision) & + (llvm::APFloat::opOverflow | llvm::APFloat::opInvalidOp)); + assert(isOk && "Failed to convert integer to floating-point!"); + (void)isOk; + return To; +} + llvm::APSInt Z3ConstraintManager::castAPSInt(const llvm::APSInt &V, QualType ToTy, uint64_t ToWidth, QualType FromTy, Index: test/Analysis/diagnostics/macros.cpp =================================================================== --- test/Analysis/diagnostics/macros.cpp +++ test/Analysis/diagnostics/macros.cpp @@ -30,8 +30,12 @@ // There are no path notes on the comparison to float types. void testDoubleMacro(double d) { +#ifdef ANALYZER_CM_Z3 + if (d == DBL_MAX) { // expected-note {{Assuming 'd' is equal to DBL_MAX}} + // expected-note@-1 {{Taking true branch}} +#else if (d == DBL_MAX) { // expected-note {{Taking true branch}} - +#endif char *p = NULL; // expected-note {{'p' initialized to a null pointer value}} *p = 7; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}} // expected-note@-1 {{Dereference of null pointer (loaded from variable 'p')}} Index: test/Analysis/float-rules.c =================================================================== --- /dev/null +++ test/Analysis/float-rules.c @@ -0,0 +1,107 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core.builtin,alpha.core.FPMath,debug.ExprInspection -verify %s +// REQUIRES: z3 + +double acos(double x); +double asin(double x); +double acosh(double x); +double atanh(double x); +double log(double x); +double log2(double x); +double log10(double x); +double log1p(double x); +double logb(double x); +double sqrt(double x); +double fmod(double x, double y); +double remainder(double x, double y); + +double domain1() { + double nan = __builtin_nan(""); + double z = 0.0; + + // -1 <= x <= 1 + z = acos(-5.0); // expected-warning{{Argument value is out of valid domain [-1, 1] for function call to acos}} + z = acos(5.0); //expected-warning{{Argument value is out of valid domain [-1, 1] for function call to acos}} + z = acos(0.0); // no-warning + z = acos(nan); // no-warning + z = asin(-5.0); // expected-warning{{Argument value is out of valid domain [-1, 1] for function call to asin}} + z = asin(5.0); // expected-warning{{Argument value is out of valid domain [-1, 1] for function call to asin}} + z = asin(0.0); // no-warning + z = asin(nan); // no-warning + + // x >= 1 + z = acosh(-5.0); // expected-warning{{Argument value is out of valid domain [1, inf) for function call to acosh}} + z = acosh(-1.0); // expected-warning{{Argument value is out of valid domain [1, inf) for function call to acosh}} + z = acosh(0.0); // expected-warning{{Argument value is out of valid domain [1, inf) for function call to acosh}} + z = acosh(1.0); // no-warning + z = acosh(nan); // no-warning + + // -1 < x < 1 + z = atanh(-1.0); // expected-warning{{Argument value is out of valid domain (-1, 1) for function call to atanh}} + z = atanh(1.0); // expected-warning{{Argument value is out of valid domain (-1, 1) for function call to atanh}} + z = atanh(0.0); // no-warning + z = atanh(nan); // no-warning + + // x >= 0 + z = log(-1.0); // expected-warning{{Argument value is out of valid domain [0, inf) for function call to log}} + z = log(-0.5); // expected-warning{{Argument value is out of valid domain [0, inf) for function call to log}} + z = log(0.0); // no-warning + z = log(nan); // no-warning + z = log2(-1.0); // expected-warning{{Argument value is out of valid domain [0, inf) for function call to log2}} + z = log2(-0.5); // expected-warning{{Argument value is out of valid domain [0, inf) for function call to log2}} + z = log2(0.0); // no-warning + z = log2(nan); // no-warning + z = log10(-1.0); // expected-warning{{Argument value is out of valid domain [0, inf) for function call to log10}} + z = log10(-0.5); // expected-warning{{Argument value is out of valid domain [0, inf) for function call to log10}} + z = log10(0.0); // no-warning + z = log10(nan); // no-warning + z = sqrt(-1.0); // expected-warning{{Argument value is out of valid domain [0, inf) for function call to sqrt}} + z = sqrt(-0.5); // expected-warning{{Argument value is out of valid domain [0, inf) for function call to sqrt}} + z = sqrt(0.0); // no-warning + z = sqrt(nan); // no-warning + + // x >= -1 + z = log1p(-5.0); // expected-warning{{Argument value is out of valid domain [-1, inf) for function call to log1p}} + z = log1p(-1.0); // no-warning + z = log1p(0.0); // no-warning + z = log1p(nan); // no-warning + + // x != 0 + z = logb(0.0); // expected-warning{{Argument value is out of valid domain (-inf, 0) U (0, inf) for function call to logb}} + z = logb(-1.0); // no-warning + z = logb(1.0); // no-warning + z = logb(nan); // no-warning + + return z; +} + +double domain2(double x) { + double nan = __builtin_nan(""); + double z = 0.0; + + // y != 0 + z = fmod(x, 0.0); // expected-warning{{Argument value is out of valid domain (-inf, 0) U (0, inf) for function call to fmod}} + z = fmod(x, 1.0); // no-warning + z = fmod(x, -1.0); // no-warning + z = fmod(x, nan); // no-warning + z = remainder(x, 0.0); // expected-warning{{Argument value is out of valid domain (-inf, 0) U (0, inf) for function call to remainder}} + z = remainder(x, 1.0); // no-warning + z = remainder(x, -1.0); // no-warning + z = remainder(x, nan); // no-warning + + return z; +} + +double domain3(double x) { + double z = 0.0; + + if (__builtin_isnan(x) || x < 0) + return z; + + z = acosh(x); // expected-warning{{Argument value is out of valid domain [1, inf) for function call to acosh}} + z = asin(x); // expected-warning{{Argument value is out of valid domain [-1, 1] for function call to asin}} + z = logb(x); // expected-warning{{Argument value is out of valid domain (-inf, 0) U (0, inf) for function call to logb}} + z = log1p(x); // no-warning + z = log(x); // no-warning + + return z; +} Index: test/Analysis/float.c =================================================================== --- /dev/null +++ test/Analysis/float.c @@ -0,0 +1,93 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core.builtin,debug.ExprInspection -verify %s +// REQUIRES: z3 + +void clang_analyzer_eval(int); + +void float1() { + float z1 = 0.0, z2 = -0.0; + clang_analyzer_eval(z1 == z2); // expected-warning{{TRUE}} +} + +void float2(float a, float b) { + clang_analyzer_eval(a == b); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a != b); // expected-warning{{UNKNOWN}} +} + +void float3(float a, float b) { + if (__builtin_isnan(a) || __builtin_isnan(b) || a < b) { + clang_analyzer_eval(a > b); // expected-warning{{FALSE}} + clang_analyzer_eval(a == b); // expected-warning{{FALSE}} + return; + } + clang_analyzer_eval(a >= b); // expected-warning{{TRUE}} + clang_analyzer_eval(a == b); // expected-warning{{UNKNOWN}} +} + +void float4(float a) { + if (__builtin_isnan(a) || a < 0.0f) + return; + clang_analyzer_eval(a >= 0.0Q); // expected-warning{{TRUE}} + clang_analyzer_eval(a >= 0.0F); // expected-warning{{TRUE}} + clang_analyzer_eval(a >= 0.0); // expected-warning{{TRUE}} + clang_analyzer_eval(a >= 0); // expected-warning{{TRUE}} +} + +void float5() { + double value = 1.0; + double pinf = __builtin_inf(); + double ninf = -__builtin_inf(); + double nan = __builtin_nan(""); + + /* NaN */ + clang_analyzer_eval(__builtin_isnan(value)); // expected-warning{{FALSE}} + clang_analyzer_eval(__builtin_isnan(nan)); // expected-warning{{TRUE}} + + clang_analyzer_eval(__builtin_isnan(0.0 / 0.0)); // expected-warning{{TRUE}} + clang_analyzer_eval(__builtin_isnan(pinf / ninf)); // expected-warning{{TRUE}} + clang_analyzer_eval(__builtin_isnan(pinf / pinf)); // expected-warning{{TRUE}} + clang_analyzer_eval(__builtin_isnan(ninf / pinf)); // expected-warning{{TRUE}} + clang_analyzer_eval(__builtin_isnan(ninf / ninf)); // expected-warning{{TRUE}} + + clang_analyzer_eval(__builtin_isnan(0.0 * ninf)); // expected-warning{{TRUE}} + clang_analyzer_eval(__builtin_isnan(0.0 * pinf)); // expected-warning{{TRUE}} + clang_analyzer_eval(__builtin_isnan(ninf * 0.0)); // expected-warning{{TRUE}} + clang_analyzer_eval(__builtin_isnan(pinf * 0.0)); // expected-warning{{TRUE}} + + clang_analyzer_eval(__builtin_isnan(nan + value)); // expected-warning{{TRUE}} + clang_analyzer_eval(__builtin_isnan(value - nan)); // expected-warning{{TRUE}} + clang_analyzer_eval(__builtin_isnan(nan * value)); // expected-warning{{TRUE}} + clang_analyzer_eval(__builtin_isnan(value / nan)); // expected-warning{{TRUE}} + + /* Infinity */ + clang_analyzer_eval(__builtin_isinf(value)); // expected-warning{{FALSE}} + clang_analyzer_eval(__builtin_isinf(pinf)); // expected-warning{{TRUE}} + clang_analyzer_eval(__builtin_isinf(ninf)); // expected-warning{{TRUE}} + clang_analyzer_eval(__builtin_isinf(value / 0.0)); // expected-warning{{TRUE}} + clang_analyzer_eval(__builtin_isinf(pinf / 0.0)); // expected-warning{{TRUE}} + clang_analyzer_eval(__builtin_isinf(ninf / 0.0)); // expected-warning{{TRUE}} + clang_analyzer_eval(value / pinf == 0.0); // expected-warning{{TRUE}} + clang_analyzer_eval(value / ninf == 0.0); // expected-warning{{TRUE}} + + /* Zero */ + clang_analyzer_eval(0.0 == -0.0); // expected-warning{{TRUE}} +} + +void float6(float a, float b) { + clang_analyzer_eval(a != 4.0 || a == 4.0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(a < 4.0 || a >= 4.0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval((a != b) == !(a == b)); // expected-warning{{UNKNOWN}} + clang_analyzer_eval((a != 4.0) == !(a == 4.0)); // expected-warning{{UNKNOWN}} +} + +void mixed() { + clang_analyzer_eval(0.0 * 0 == 0.0); // expected-warning{{TRUE}} + clang_analyzer_eval(1.0 * 0 == 0.0); // expected-warning{{TRUE}} + clang_analyzer_eval(1.0 * 1 == 1.0); // expected-warning{{TRUE}} + clang_analyzer_eval((5 * 5) * 1.0 == 25); // expected-warning{{TRUE}} +} + +void nan1(double a) { + if (a == a) + return; + clang_analyzer_eval(__builtin_isnan(a)); // expected-warning{{TRUE}} +} Index: test/Analysis/inline.cpp =================================================================== --- test/Analysis/inline.cpp +++ test/Analysis/inline.cpp @@ -285,11 +285,11 @@ } void testFloatReference() { - clang_analyzer_eval(defaultFloatReference(1) == -1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(defaultFloatReference() == -42); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(defaultFloatReference(1) == -1); // expected-warning{{TRUE}} + clang_analyzer_eval(defaultFloatReference() == -42); // expected-warning{{TRUE}} - clang_analyzer_eval(defaultFloatReferenceZero(1) == -1); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(defaultFloatReferenceZero() == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(defaultFloatReferenceZero(1) == -1); // expected-warning{{TRUE}} + clang_analyzer_eval(defaultFloatReferenceZero() == 0); // expected-warning{{TRUE}} } char defaultString(const char *s = "abc") { Index: test/Analysis/operator-calls.cpp =================================================================== --- test/Analysis/operator-calls.cpp +++ test/Analysis/operator-calls.cpp @@ -81,8 +81,8 @@ void test(int coin) { // Force a cache-out when we try to conjure a temporary region for the operator call. // ...then, don't crash. - clang_analyzer_eval(+(coin ? getSmallOpaque() : getSmallOpaque())); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(+(coin ? getLargeOpaque() : getLargeOpaque())); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(+(coin ? getSmallOpaque() : getSmallOpaque())); // expected-warning{{TRUE}} + clang_analyzer_eval(+(coin ? getLargeOpaque() : getLargeOpaque())); // expected-warning{{TRUE}} } }