summaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Core
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2017-04-16 16:02:28 +0000
committerDimitry Andric <dim@FreeBSD.org>2017-04-16 16:02:28 +0000
commit7442d6faa2719e4e7d33a7021c406c5a4facd74d (patch)
treec72b9241553fc9966179aba84f90f17bfa9235c3 /lib/StaticAnalyzer/Core
parentb52119637f743680a99710ce5fdb6646da2772af (diff)
Notes
Diffstat (limited to 'lib/StaticAnalyzer/Core')
-rw-r--r--lib/StaticAnalyzer/Core/AnalyzerOptions.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/BugReporterVisitors.cpp8
-rw-r--r--lib/StaticAnalyzer/Core/CMakeLists.txt17
-rw-r--r--lib/StaticAnalyzer/Core/CallEvent.cpp81
-rw-r--r--lib/StaticAnalyzer/Core/CheckerManager.cpp12
-rw-r--r--lib/StaticAnalyzer/Core/ConstraintManager.cpp4
-rw-r--r--lib/StaticAnalyzer/Core/DynamicTypeMap.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp156
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineC.cpp9
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCXX.cpp9
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineObjC.cpp4
-rw-r--r--lib/StaticAnalyzer/Core/MemRegion.cpp62
-rw-r--r--lib/StaticAnalyzer/Core/ProgramState.cpp15
-rw-r--r--lib/StaticAnalyzer/Core/RangeConstraintManager.cpp102
-rw-r--r--lib/StaticAnalyzer/Core/RangedConstraintManager.cpp204
-rw-r--r--lib/StaticAnalyzer/Core/RangedConstraintManager.h (renamed from lib/StaticAnalyzer/Core/SimpleConstraintManager.h)69
-rw-r--r--lib/StaticAnalyzer/Core/RegionStore.cpp10
-rw-r--r--lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp230
-rw-r--r--lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp102
-rw-r--r--lib/StaticAnalyzer/Core/Store.cpp35
-rw-r--r--lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp1618
21 files changed, 2317 insertions, 434 deletions
diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
index 15422633ba33c..45ef612ee1d58 100644
--- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -230,7 +230,7 @@ bool AnalyzerOptions::shouldSuppressInlinedDefensiveChecks() {
bool AnalyzerOptions::shouldSuppressFromCXXStandardLibrary() {
return getBooleanOption(SuppressFromCXXStandardLibrary,
"suppress-c++-stdlib",
- /* Default = */ false);
+ /* Default = */ true);
}
bool AnalyzerOptions::shouldReportIssuesInMainSourceFile() {
diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index c3c3f2ff76ecf..7309741d22e39 100644
--- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -1027,7 +1027,7 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N,
R = LVState->getSVal(Inner, LVNode->getLocationContext()).getAsRegion();
// If this is a C++ reference to a null pointer, we are tracking the
- // pointer. In additon, we should find the store at which the reference
+ // pointer. In addition, we should find the store at which the reference
// got initialized.
if (const MemRegion *RR = getLocationRegionIfReference(Inner, N)) {
if (Optional<KnownSVal> KV = LVal.getAs<KnownSVal>())
@@ -1290,7 +1290,7 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator(
break;
case Stmt::BinaryOperatorClass:
// When we encounter a logical operator (&& or ||) as a CFG terminator,
- // then the condition is actually its LHS; otheriwse, we'd encounter
+ // then the condition is actually its LHS; otherwise, we'd encounter
// the parent, such as if-statement, as a terminator.
const auto *BO = cast<BinaryOperator>(Term);
assert(BO->isLogicalOp() &&
@@ -1659,7 +1659,7 @@ LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC,
// The analyzer issues a false use-after-free when std::list::pop_front
// or std::list::pop_back are called multiple times because we cannot
- // reason about the internal invariants of the datastructure.
+ // reason about the internal invariants of the data structure.
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) {
const CXXRecordDecl *CD = MD->getParent();
if (CD->getName() == "list") {
@@ -1690,7 +1690,7 @@ LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC,
// and
// std::u16string s; s += u'a';
// because we cannot reason about the internal invariants of the
- // datastructure.
+ // data structure.
if (CD->getName() == "basic_string") {
BR.markInvalid(getTag(), nullptr);
return nullptr;
diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt
index aaffb0b82ce00..85878f5e96ee7 100644
--- a/lib/StaticAnalyzer/Core/CMakeLists.txt
+++ b/lib/StaticAnalyzer/Core/CMakeLists.txt
@@ -1,5 +1,12 @@
set(LLVM_LINK_COMPONENTS support)
+# Link Z3 if the user wants to build it.
+if(CLANG_ANALYZER_WITH_Z3)
+ set(Z3_LINK_FILES ${Z3_LIBRARIES})
+else()
+ set(Z3_LINK_FILES "")
+endif()
+
add_clang_library(clangStaticAnalyzerCore
APSIntType.cpp
AnalysisManager.cpp
@@ -34,6 +41,7 @@ add_clang_library(clangStaticAnalyzerCore
PlistDiagnostics.cpp
ProgramState.cpp
RangeConstraintManager.cpp
+ RangedConstraintManager.cpp
RegionStore.cpp
SValBuilder.cpp
SVals.cpp
@@ -42,6 +50,7 @@ add_clang_library(clangStaticAnalyzerCore
Store.cpp
SubEngine.cpp
SymbolManager.cpp
+ Z3ConstraintManager.cpp
LINK_LIBS
clangAST
@@ -49,4 +58,12 @@ add_clang_library(clangStaticAnalyzerCore
clangBasic
clangLex
clangRewrite
+ ${Z3_LINK_FILES}
)
+
+if(CLANG_ANALYZER_WITH_Z3)
+ target_include_directories(clangStaticAnalyzerCore SYSTEM
+ PRIVATE
+ ${Z3_INCLUDE_DIR}
+ )
+endif()
diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp
index 420e2a6b5c8c2..ee761689f479b 100644
--- a/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -212,9 +212,12 @@ ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit,
bool CallEvent::isCalled(const CallDescription &CD) const {
assert(getKind() != CE_ObjCMessage && "Obj-C methods are not supported");
- if (!CD.II)
+ if (!CD.IsLookupDone) {
+ CD.IsLookupDone = true;
CD.II = &getState()->getStateManager().getContext().Idents.get(CD.FuncName);
- if (getCalleeIdentifier() != CD.II)
+ }
+ const IdentifierInfo *II = getCalleeIdentifier();
+ if (!II || II != CD.II)
return false;
return (CD.RequiredArgs == CallDescription::NoArgRequirement ||
CD.RequiredArgs == getNumArgs());
@@ -692,13 +695,15 @@ void ObjCMethodCall::getExtraInvalidatedValues(
if (const ObjCPropertyDecl *PropDecl = getAccessedProperty()) {
if (const ObjCIvarDecl *PropIvar = PropDecl->getPropertyIvarDecl()) {
SVal IvarLVal = getState()->getLValue(PropIvar, getReceiverSVal());
- const MemRegion *IvarRegion = IvarLVal.getAsRegion();
- ETraits->setTrait(
+ if (const MemRegion *IvarRegion = IvarLVal.getAsRegion()) {
+ ETraits->setTrait(
IvarRegion,
RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
- ETraits->setTrait(IvarRegion,
- RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
- Values.push_back(IvarLVal);
+ ETraits->setTrait(
+ IvarRegion,
+ RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
+ Values.push_back(IvarLVal);
+ }
return;
}
}
@@ -896,6 +901,38 @@ bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl,
llvm_unreachable("The while loop should always terminate.");
}
+static const ObjCMethodDecl *findDefiningRedecl(const ObjCMethodDecl *MD) {
+ if (!MD)
+ return MD;
+
+ // Find the redeclaration that defines the method.
+ if (!MD->hasBody()) {
+ for (auto I : MD->redecls())
+ if (I->hasBody())
+ MD = cast<ObjCMethodDecl>(I);
+ }
+ return MD;
+}
+
+static bool isCallToSelfClass(const ObjCMessageExpr *ME) {
+ const Expr* InstRec = ME->getInstanceReceiver();
+ if (!InstRec)
+ return false;
+ const auto *InstRecIg = dyn_cast<DeclRefExpr>(InstRec->IgnoreParenImpCasts());
+
+ // Check that receiver is called 'self'.
+ if (!InstRecIg || !InstRecIg->getFoundDecl() ||
+ !InstRecIg->getFoundDecl()->getName().equals("self"))
+ return false;
+
+ // Check that the method name is 'class'.
+ if (ME->getSelector().getNumArgs() != 0 ||
+ !ME->getSelector().getNameForSlot(0).equals("class"))
+ return false;
+
+ return true;
+}
+
RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
const ObjCMessageExpr *E = getOriginExpr();
assert(E);
@@ -910,6 +947,7 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
const MemRegion *Receiver = nullptr;
if (!SupersType.isNull()) {
+ // The receiver is guaranteed to be 'super' in this case.
// Super always means the type of immediate predecessor to the method
// where the call occurs.
ReceiverT = cast<ObjCObjectPointerType>(SupersType);
@@ -921,7 +959,7 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
DynamicTypeInfo DTI = getDynamicTypeInfo(getState(), Receiver);
QualType DynType = DTI.getType();
CanBeSubClassed = DTI.canBeASubClass();
- ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType);
+ ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType.getCanonicalType());
if (ReceiverT && CanBeSubClassed)
if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl())
@@ -929,7 +967,32 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
CanBeSubClassed = false;
}
- // Lookup the method implementation.
+ // Handle special cases of '[self classMethod]' and
+ // '[[self class] classMethod]', which are treated by the compiler as
+ // instance (not class) messages. We will statically dispatch to those.
+ if (auto *PT = dyn_cast_or_null<ObjCObjectPointerType>(ReceiverT)) {
+ // For [self classMethod], return the compiler visible declaration.
+ if (PT->getObjectType()->isObjCClass() &&
+ Receiver == getSelfSVal().getAsRegion())
+ return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
+
+ // Similarly, handle [[self class] classMethod].
+ // TODO: We are currently doing a syntactic match for this pattern with is
+ // limiting as the test cases in Analysis/inlining/InlineObjCClassMethod.m
+ // shows. A better way would be to associate the meta type with the symbol
+ // using the dynamic type info tracking and use it here. We can add a new
+ // SVal for ObjC 'Class' values that know what interface declaration they
+ // come from. Then 'self' in a class method would be filled in with
+ // something meaningful in ObjCMethodCall::getReceiverSVal() and we could
+ // do proper dynamic dispatch for class methods just like we do for
+ // instance methods now.
+ if (E->getInstanceReceiver())
+ if (const auto *M = dyn_cast<ObjCMessageExpr>(E->getInstanceReceiver()))
+ if (isCallToSelfClass(M))
+ return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
+ }
+
+ // Lookup the instance method implementation.
if (ReceiverT)
if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) {
// Repeatedly calling lookupPrivateMethod() is expensive, especially
diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp
index 79e204cdafec9..49f3edef2a2d3 100644
--- a/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -521,17 +521,19 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
/// \brief Run checkers for region changes.
ProgramStateRef
CheckerManager::runCheckersForRegionChanges(ProgramStateRef state,
- const InvalidatedSymbols *invalidated,
- ArrayRef<const MemRegion *> ExplicitRegions,
- ArrayRef<const MemRegion *> Regions,
- const CallEvent *Call) {
+ const InvalidatedSymbols *invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
+ const CallEvent *Call) {
for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) {
// If any checker declares the state infeasible (or if it starts that way),
// bail out.
if (!state)
return nullptr;
state = RegionChangesCheckers[i](state, invalidated,
- ExplicitRegions, Regions, Call);
+ ExplicitRegions, Regions,
+ LCtx, Call);
}
return state;
}
diff --git a/lib/StaticAnalyzer/Core/ConstraintManager.cpp b/lib/StaticAnalyzer/Core/ConstraintManager.cpp
index b7db8333aaac6..8de2b0e8d271d 100644
--- a/lib/StaticAnalyzer/Core/ConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/ConstraintManager.cpp
@@ -20,8 +20,8 @@ ConstraintManager::~ConstraintManager() {}
static DefinedSVal getLocFromSymbol(const ProgramStateRef &State,
SymbolRef Sym) {
- const MemRegion *R = State->getStateManager().getRegionManager()
- .getSymbolicRegion(Sym);
+ const MemRegion *R =
+ State->getStateManager().getRegionManager().getSymbolicRegion(Sym);
return loc::MemRegionVal(R);
}
diff --git a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp
index fd35b664a9127..a01ff36a8aae0 100644
--- a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp
+++ b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
//
// This file defines APIs that track and query dynamic type information. This
-// information can be used to devirtualize calls during the symbolic exection
+// information can be used to devirtualize calls during the symbolic execution
// or do type checking.
//
//===----------------------------------------------------------------------===//
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index d563f8e9eac02..9e6ec09010e91 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -182,19 +182,25 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
ProgramStateRef
ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State,
const LocationContext *LC,
- const Expr *Ex,
+ const Expr *InitWithAdjustments,
const Expr *Result) {
- SVal V = State->getSVal(Ex, LC);
+ // FIXME: This function is a hack that works around the quirky AST
+ // we're often having with respect to C++ temporaries. If only we modelled
+ // the actual execution order of statements properly in the CFG,
+ // all the hassle with adjustments would not be necessary,
+ // and perhaps the whole function would be removed.
+ SVal InitValWithAdjustments = State->getSVal(InitWithAdjustments, LC);
if (!Result) {
// If we don't have an explicit result expression, we're in "if needed"
// mode. Only create a region if the current value is a NonLoc.
- if (!V.getAs<NonLoc>())
+ if (!InitValWithAdjustments.getAs<NonLoc>())
return State;
- Result = Ex;
+ Result = InitWithAdjustments;
} else {
// We need to create a region no matter what. For sanity, make sure we don't
// try to stuff a Loc into a non-pointer temporary region.
- assert(!V.getAs<Loc>() || Loc::isLocType(Result->getType()) ||
+ assert(!InitValWithAdjustments.getAs<Loc>() ||
+ Loc::isLocType(Result->getType()) ||
Result->getType()->isMemberPointerType());
}
@@ -226,7 +232,8 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State,
SmallVector<const Expr *, 2> CommaLHSs;
SmallVector<SubobjectAdjustment, 2> Adjustments;
- const Expr *Init = Ex->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments);
+ const Expr *Init = InitWithAdjustments->skipRValueSubobjectAdjustments(
+ CommaLHSs, Adjustments);
const TypedValueRegion *TR = nullptr;
if (const MaterializeTemporaryExpr *MT =
@@ -241,6 +248,7 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State,
TR = MRMgr.getCXXTempObjectRegion(Init, LC);
SVal Reg = loc::MemRegionVal(TR);
+ SVal BaseReg = Reg;
// Make the necessary adjustments to obtain the sub-object.
for (auto I = Adjustments.rbegin(), E = Adjustments.rend(); I != E; ++I) {
@@ -254,19 +262,47 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State,
break;
case SubobjectAdjustment::MemberPointerAdjustment:
// FIXME: Unimplemented.
- State->bindDefault(Reg, UnknownVal());
+ State = State->bindDefault(Reg, UnknownVal(), LC);
return State;
}
}
- // Try to recover some path sensitivity in case we couldn't compute the value.
- if (V.isUnknown())
- V = getSValBuilder().conjureSymbolVal(Result, LC, TR->getValueType(),
- currBldrCtx->blockCount());
- // Bind the value of the expression to the sub-object region, and then bind
- // the sub-object region to our expression.
- State = State->bindLoc(Reg, V);
+ // What remains is to copy the value of the object to the new region.
+ // FIXME: In other words, what we should always do is copy value of the
+ // Init expression (which corresponds to the bigger object) to the whole
+ // temporary region TR. However, this value is often no longer present
+ // in the Environment. If it has disappeared, we instead invalidate TR.
+ // Still, what we can do is assign the value of expression Ex (which
+ // corresponds to the sub-object) to the TR's sub-region Reg. At least,
+ // values inside Reg would be correct.
+ SVal InitVal = State->getSVal(Init, LC);
+ if (InitVal.isUnknown()) {
+ InitVal = getSValBuilder().conjureSymbolVal(Result, LC, Init->getType(),
+ currBldrCtx->blockCount());
+ State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false);
+
+ // Then we'd need to take the value that certainly exists and bind it over.
+ if (InitValWithAdjustments.isUnknown()) {
+ // Try to recover some path sensitivity in case we couldn't
+ // compute the value.
+ InitValWithAdjustments = getSValBuilder().conjureSymbolVal(
+ Result, LC, InitWithAdjustments->getType(),
+ currBldrCtx->blockCount());
+ }
+ State =
+ State->bindLoc(Reg.castAs<Loc>(), InitValWithAdjustments, LC, false);
+ } else {
+ State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false);
+ }
+
+ // The result expression would now point to the correct sub-region of the
+ // newly created temporary region. Do this last in order to getSVal of Init
+ // correctly in case (Result == Init).
State = State->BindExpr(Result, LC, Reg);
+
+ // Notify checkers once for two bindLoc()s.
+ State = processRegionChange(State, TR, LC);
+
return State;
}
@@ -286,9 +322,11 @@ ExprEngine::processRegionChanges(ProgramStateRef state,
const InvalidatedSymbols *invalidated,
ArrayRef<const MemRegion *> Explicits,
ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
const CallEvent *Call) {
return getCheckerManager().runCheckersForRegionChanges(state, invalidated,
- Explicits, Regions, Call);
+ Explicits, Regions,
+ LCtx, Call);
}
void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State,
@@ -613,7 +651,15 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
const MemRegion *Region = dest.castAs<loc::MemRegionVal>().getRegion();
if (varType->isReferenceType()) {
- Region = state->getSVal(Region).getAsRegion()->getBaseRegion();
+ const MemRegion *ValueRegion = state->getSVal(Region).getAsRegion();
+ if (!ValueRegion) {
+ // FIXME: This should not happen. The language guarantees a presence
+ // of a valid initializer here, so the reference shall not be undefined.
+ // It seems that we're calling destructors over variables that
+ // were not initialized yet.
+ return;
+ }
+ Region = ValueRegion->getBaseRegion();
varType = cast<TypedValueRegion>(Region)->getValueType();
}
@@ -767,7 +813,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
assert(!isa<Expr>(S) || S == cast<Expr>(S)->IgnoreParens());
switch (S->getStmtClass()) {
- // C++ and ARC stuff we don't support yet.
+ // C++, OpenMP and ARC stuff we don't support yet.
case Expr::ObjCIndirectCopyRestoreExprClass:
case Stmt::CXXDependentScopeMemberExprClass:
case Stmt::CXXInheritedCtorInitExprClass:
@@ -790,41 +836,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::FunctionParmPackExprClass:
case Stmt::CoroutineBodyStmtClass:
case Stmt::CoawaitExprClass:
+ case Stmt::DependentCoawaitExprClass:
case Stmt::CoreturnStmtClass:
case Stmt::CoyieldExprClass:
case Stmt::SEHTryStmtClass:
case Stmt::SEHExceptStmtClass:
case Stmt::SEHLeaveStmtClass:
- case Stmt::SEHFinallyStmtClass: {
- const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
- Engine.addAbortedBlock(node, currBldrCtx->getBlock());
- break;
- }
-
- case Stmt::ParenExprClass:
- llvm_unreachable("ParenExprs already handled.");
- case Stmt::GenericSelectionExprClass:
- llvm_unreachable("GenericSelectionExprs already handled.");
- // Cases that should never be evaluated simply because they shouldn't
- // appear in the CFG.
- case Stmt::BreakStmtClass:
- case Stmt::CaseStmtClass:
- case Stmt::CompoundStmtClass:
- case Stmt::ContinueStmtClass:
- case Stmt::CXXForRangeStmtClass:
- case Stmt::DefaultStmtClass:
- case Stmt::DoStmtClass:
- case Stmt::ForStmtClass:
- case Stmt::GotoStmtClass:
- case Stmt::IfStmtClass:
- case Stmt::IndirectGotoStmtClass:
- case Stmt::LabelStmtClass:
- case Stmt::NoStmtClass:
- case Stmt::NullStmtClass:
- case Stmt::SwitchStmtClass:
- case Stmt::WhileStmtClass:
- case Expr::MSDependentExistsStmtClass:
- case Stmt::CapturedStmtClass:
+ case Stmt::SEHFinallyStmtClass:
case Stmt::OMPParallelDirectiveClass:
case Stmt::OMPSimdDirectiveClass:
case Stmt::OMPForDirectiveClass:
@@ -872,6 +890,36 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass:
case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass:
case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:
+ case Stmt::CapturedStmtClass:
+ {
+ const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
+ Engine.addAbortedBlock(node, currBldrCtx->getBlock());
+ break;
+ }
+
+ case Stmt::ParenExprClass:
+ llvm_unreachable("ParenExprs already handled.");
+ case Stmt::GenericSelectionExprClass:
+ llvm_unreachable("GenericSelectionExprs already handled.");
+ // Cases that should never be evaluated simply because they shouldn't
+ // appear in the CFG.
+ case Stmt::BreakStmtClass:
+ case Stmt::CaseStmtClass:
+ case Stmt::CompoundStmtClass:
+ case Stmt::ContinueStmtClass:
+ case Stmt::CXXForRangeStmtClass:
+ case Stmt::DefaultStmtClass:
+ case Stmt::DoStmtClass:
+ case Stmt::ForStmtClass:
+ case Stmt::GotoStmtClass:
+ case Stmt::IfStmtClass:
+ case Stmt::IndirectGotoStmtClass:
+ case Stmt::LabelStmtClass:
+ case Stmt::NoStmtClass:
+ case Stmt::NullStmtClass:
+ case Stmt::SwitchStmtClass:
+ case Stmt::WhileStmtClass:
+ case Expr::MSDependentExistsStmtClass:
llvm_unreachable("Stmt should not be in analyzer evaluation loop");
case Stmt::ObjCSubscriptRefExprClass:
@@ -2165,7 +2213,9 @@ public:
// (3) We are binding to a MemRegion with stack storage that the store
// does not understand.
ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State,
- SVal Loc, SVal Val) {
+ SVal Loc,
+ SVal Val,
+ const LocationContext *LCtx) {
// Are we storing to something that causes the value to "escape"?
bool escapes = true;
@@ -2181,7 +2231,7 @@ ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State,
// same state.
SVal StoredVal = State->getSVal(regionLoc->getRegion());
if (StoredVal != Val)
- escapes = (State == (State->bindLoc(*regionLoc, Val)));
+ escapes = (State == (State->bindLoc(*regionLoc, Val, LCtx)));
}
}
@@ -2278,7 +2328,7 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
const ProgramPoint L = PostStore(StoreE, LC, /*Loc*/nullptr,
/*tag*/nullptr);
ProgramStateRef state = Pred->getState();
- state = processPointerEscapedOnBind(state, location, Val);
+ state = processPointerEscapedOnBind(state, location, Val, LC);
Bldr.generateNode(L, state, Pred);
return;
}
@@ -2288,13 +2338,13 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
ExplodedNode *PredI = *I;
ProgramStateRef state = PredI->getState();
- state = processPointerEscapedOnBind(state, location, Val);
+ state = processPointerEscapedOnBind(state, location, Val, LC);
// When binding the value, pass on the hint that this is a initialization.
// For initializations, we do not need to inform clients of region
// changes.
state = state->bindLoc(location.castAs<Loc>(),
- Val, /* notifyChanges = */ !atDeclInit);
+ Val, LC, /* notifyChanges = */ !atDeclInit);
const MemRegion *LocReg = nullptr;
if (Optional<loc::MemRegionVal> LocRegVal =
@@ -2520,7 +2570,7 @@ void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred,
assert (!X.getAs<NonLoc>()); // Should be an Lval, or unknown, undef.
if (Optional<Loc> LV = X.getAs<Loc>())
- state = state->bindLoc(*LV, UnknownVal());
+ state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext());
}
Bldr.generateNode(A, Pred, state);
diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 89fab1d56af00..8f720a2067b15 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -227,12 +227,13 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
if (capturedR != originalR) {
SVal originalV;
+ const LocationContext *LCtx = Pred->getLocationContext();
if (copyExpr) {
- originalV = State->getSVal(copyExpr, Pred->getLocationContext());
+ originalV = State->getSVal(copyExpr, LCtx);
} else {
originalV = State->getSVal(loc::MemRegionVal(originalR));
}
- State = State->bindLoc(loc::MemRegionVal(capturedR), originalV);
+ State = State->bindLoc(loc::MemRegionVal(capturedR), originalV, LCtx);
}
}
}
@@ -534,7 +535,7 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL,
} else {
assert(isa<InitListExpr>(Init));
Loc CLLoc = State->getLValue(CL, LCtx);
- State = State->bindLoc(CLLoc, V);
+ State = State->bindLoc(CLLoc, V, LCtx);
if (CL->isGLValue())
V = CLLoc;
@@ -1053,7 +1054,7 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,
// Conjure a new symbol if necessary to recover precision.
if (Result.isUnknown()){
DefinedOrUnknownSVal SymVal =
- svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx,
+ svalBuilder.conjureSymbolVal(nullptr, U, LCtx,
currBldrCtx->blockCount());
Result = SymVal;
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 7e9b2033ca373..03e0095d0e837 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -317,7 +317,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
// actually make things worse. Placement new makes this tricky as well,
// since it's then possible to be initializing one part of a multi-
// dimensional array.
- State = State->bindDefault(loc::MemRegionVal(Target), ZeroVal);
+ State = State->bindDefault(loc::MemRegionVal(Target), ZeroVal, LCtx);
Bldr.generateNode(CE, *I, State, /*tag=*/nullptr,
ProgramPoint::PreStmtKind);
}
@@ -512,7 +512,8 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
if (CNE->isArray()) {
// FIXME: allocating an array requires simulating the constructors.
// For now, just return a symbolicated region.
- const MemRegion *NewReg = symVal.castAs<loc::MemRegionVal>().getRegion();
+ const SubRegion *NewReg =
+ symVal.castAs<loc::MemRegionVal>().getRegionAs<SubRegion>();
QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType();
const ElementRegion *EleReg =
getStoreManager().GetElementZeroRegion(NewReg, ObjTy);
@@ -572,7 +573,7 @@ void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS,
SVal V = svalBuilder.conjureSymbolVal(CS, LCtx, VD->getType(),
currBldrCtx->blockCount());
ProgramStateRef state = Pred->getState();
- state = state->bindLoc(state->getLValue(VD, LCtx), V);
+ state = state->bindLoc(state->getLValue(VD, LCtx), V, LCtx);
StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
Bldr.generateNode(CS, Pred, state);
@@ -627,7 +628,7 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred,
InitVal = State->getSVal(SizeExpr, LocCtxt);
}
- State = State->bindLoc(FieldLoc, InitVal);
+ State = State->bindLoc(FieldLoc, InitVal, LocCtxt);
}
// Decay the Loc into an RValue, because there might be a
diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index 92c5fe6b6f1a3..f5e64f4a5a8c6 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -115,11 +115,11 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
SymbolRef Sym = SymMgr.conjureSymbol(elem, LCtx, T,
currBldrCtx->blockCount());
SVal V = svalBuilder.makeLoc(Sym);
- hasElems = hasElems->bindLoc(elementV, V);
+ hasElems = hasElems->bindLoc(elementV, V, LCtx);
// Bind the location to 'nil' on the false branch.
SVal nilV = svalBuilder.makeIntVal(0, T);
- noElems = noElems->bindLoc(elementV, nilV);
+ noElems = noElems->bindLoc(elementV, nilV, LCtx);
}
// Create the new nodes.
diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp
index d6e8fe5b51b36..7bc186d5b9941 100644
--- a/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -31,54 +31,56 @@ using namespace ento;
// MemRegion Construction.
//===----------------------------------------------------------------------===//
-template <typename RegionTy, typename A1>
-RegionTy* MemRegionManager::getSubRegion(const A1 a1,
- const MemRegion *superRegion) {
+template <typename RegionTy, typename SuperTy, typename Arg1Ty>
+RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1,
+ const SuperTy *superRegion) {
llvm::FoldingSetNodeID ID;
- RegionTy::ProfileRegion(ID, a1, superRegion);
+ RegionTy::ProfileRegion(ID, arg1, superRegion);
void *InsertPos;
RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID,
InsertPos));
if (!R) {
R = A.Allocate<RegionTy>();
- new (R) RegionTy(a1, superRegion);
+ new (R) RegionTy(arg1, superRegion);
Regions.InsertNode(R, InsertPos);
}
return R;
}
-template <typename RegionTy, typename A1, typename A2>
-RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2,
- const MemRegion *superRegion) {
+template <typename RegionTy, typename SuperTy, typename Arg1Ty, typename Arg2Ty>
+RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2,
+ const SuperTy *superRegion) {
llvm::FoldingSetNodeID ID;
- RegionTy::ProfileRegion(ID, a1, a2, superRegion);
+ RegionTy::ProfileRegion(ID, arg1, arg2, superRegion);
void *InsertPos;
RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID,
InsertPos));
if (!R) {
R = A.Allocate<RegionTy>();
- new (R) RegionTy(a1, a2, superRegion);
+ new (R) RegionTy(arg1, arg2, superRegion);
Regions.InsertNode(R, InsertPos);
}
return R;
}
-template <typename RegionTy, typename A1, typename A2, typename A3>
-RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, const A3 a3,
- const MemRegion *superRegion) {
+template <typename RegionTy, typename SuperTy,
+ typename Arg1Ty, typename Arg2Ty, typename Arg3Ty>
+RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2,
+ const Arg3Ty arg3,
+ const SuperTy *superRegion) {
llvm::FoldingSetNodeID ID;
- RegionTy::ProfileRegion(ID, a1, a2, a3, superRegion);
+ RegionTy::ProfileRegion(ID, arg1, arg2, arg3, superRegion);
void *InsertPos;
RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID,
InsertPos));
if (!R) {
R = A.Allocate<RegionTy>();
- new (R) RegionTy(a1, a2, a3, superRegion);
+ new (R) RegionTy(arg1, arg2, arg3, superRegion);
Regions.InsertNode(R, InsertPos);
}
@@ -180,8 +182,8 @@ DefinedOrUnknownSVal StringRegion::getExtent(SValBuilder &svalBuilder) const {
svalBuilder.getArrayIndexType());
}
-ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const MemRegion* sReg)
- : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {}
+ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg)
+ : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {}
const ObjCIvarDecl *ObjCIvarRegion::getDecl() const {
return cast<ObjCIvarDecl>(D);
@@ -379,10 +381,8 @@ void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const {
//===----------------------------------------------------------------------===//
void GlobalsSpaceRegion::anchor() { }
-void HeapSpaceRegion::anchor() { }
-void UnknownSpaceRegion::anchor() { }
-void StackLocalsSpaceRegion::anchor() { }
-void StackArgumentsSpaceRegion::anchor() { }
+void NonStaticGlobalSpaceRegion::anchor() { }
+void StackSpaceRegion::anchor() { }
void TypedRegion::anchor() { }
void TypedValueRegion::anchor() { }
void CodeTextRegion::anchor() { }
@@ -737,12 +737,14 @@ const CodeSpaceRegion *MemRegionManager::getCodeRegion() {
// Constructing regions.
//===----------------------------------------------------------------------===//
const StringRegion* MemRegionManager::getStringRegion(const StringLiteral* Str){
- return getSubRegion<StringRegion>(Str, getGlobalsRegion());
+ return getSubRegion<StringRegion>(
+ Str, cast<GlobalInternalSpaceRegion>(getGlobalsRegion()));
}
const ObjCStringRegion *
MemRegionManager::getObjCStringRegion(const ObjCStringLiteral* Str){
- return getSubRegion<ObjCStringRegion>(Str, getGlobalsRegion());
+ return getSubRegion<ObjCStringRegion>(
+ Str, cast<GlobalInternalSpaceRegion>(getGlobalsRegion()));
}
/// Look through a chain of LocationContexts to either find the
@@ -871,7 +873,7 @@ const BlockDataRegion *
MemRegionManager::getBlockDataRegion(const BlockCodeRegion *BC,
const LocationContext *LC,
unsigned blockCount) {
- const MemRegion *sReg = nullptr;
+ const MemSpaceRegion *sReg = nullptr;
const BlockDecl *BD = BC->getDecl();
if (!BD->hasCaptures()) {
// This handles 'static' blocks.
@@ -904,7 +906,7 @@ MemRegionManager::getCXXStaticTempObjectRegion(const Expr *Ex) {
const CompoundLiteralRegion*
MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr *CL,
const LocationContext *LC) {
- const MemRegion *sReg = nullptr;
+ const MemSpaceRegion *sReg = nullptr;
if (CL->isFileScope())
sReg = getGlobalsRegion();
@@ -919,7 +921,7 @@ MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr *CL,
const ElementRegion*
MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx,
- const MemRegion* superRegion,
+ const SubRegion* superRegion,
ASTContext &Ctx){
QualType T = Ctx.getCanonicalType(elementType).getUnqualifiedType();
@@ -962,13 +964,13 @@ const SymbolicRegion *MemRegionManager::getSymbolicHeapRegion(SymbolRef Sym) {
const FieldRegion*
MemRegionManager::getFieldRegion(const FieldDecl *d,
- const MemRegion* superRegion){
+ const SubRegion* superRegion){
return getSubRegion<FieldRegion>(d, superRegion);
}
const ObjCIvarRegion*
MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl *d,
- const MemRegion* superRegion) {
+ const SubRegion* superRegion) {
return getSubRegion<ObjCIvarRegion>(d, superRegion);
}
@@ -1004,7 +1006,7 @@ static bool isValidBaseClass(const CXXRecordDecl *BaseClass,
const CXXBaseObjectRegion *
MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD,
- const MemRegion *Super,
+ const SubRegion *Super,
bool IsVirtual) {
if (isa<TypedValueRegion>(Super)) {
assert(isValidBaseClass(RD, dyn_cast<TypedValueRegion>(Super), IsVirtual));
@@ -1015,7 +1017,7 @@ MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD,
// are different.
while (const CXXBaseObjectRegion *Base =
dyn_cast<CXXBaseObjectRegion>(Super)) {
- Super = Base->getSuperRegion();
+ Super = cast<SubRegion>(Base->getSuperRegion());
}
assert(Super && !isa<MemSpaceRegion>(Super));
}
diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp
index 03ace35965cba..31556c792fc5b 100644
--- a/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -111,24 +111,29 @@ ProgramStateManager::removeDeadBindings(ProgramStateRef state,
return ConstraintMgr->removeDeadBindings(Result, SymReaper);
}
-ProgramStateRef ProgramState::bindLoc(Loc LV, SVal V, bool notifyChanges) const {
+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));
const MemRegion *MR = LV.getAsRegion();
if (MR && Mgr.getOwningEngine() && notifyChanges)
- return Mgr.getOwningEngine()->processRegionChange(newState, MR);
+ return Mgr.getOwningEngine()->processRegionChange(newState, MR, LCtx);
return newState;
}
-ProgramStateRef ProgramState::bindDefault(SVal loc, SVal V) const {
+ProgramStateRef ProgramState::bindDefault(SVal loc,
+ SVal V,
+ const LocationContext *LCtx) const {
ProgramStateManager &Mgr = getStateManager();
const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion();
const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V);
ProgramStateRef new_state = makeWithStore(newStore);
return Mgr.getOwningEngine() ?
- Mgr.getOwningEngine()->processRegionChange(new_state, R) :
+ Mgr.getOwningEngine()->processRegionChange(new_state, R, LCtx) :
new_state;
}
@@ -202,7 +207,7 @@ ProgramState::invalidateRegionsImpl(ValueList Values,
}
return Eng->processRegionChanges(newState, IS, TopLevelInvalidated,
- Invalidated, Call);
+ Invalidated, LCtx, Call);
}
const StoreRef &newStore =
diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index 15073bb82b366..e0ad2d8ad45c2 100644
--- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -12,7 +12,7 @@
//
//===----------------------------------------------------------------------===//
-#include "SimpleConstraintManager.h"
+#include "RangedConstraintManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
@@ -282,12 +282,31 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintRange,
RangeSet))
namespace {
-class RangeConstraintManager : public SimpleConstraintManager {
- RangeSet getRange(ProgramStateRef State, SymbolRef Sym);
-
+class RangeConstraintManager : public RangedConstraintManager {
public:
RangeConstraintManager(SubEngine *SE, SValBuilder &SVB)
- : SimpleConstraintManager(SE, SVB) {}
+ : RangedConstraintManager(SE, SVB) {}
+
+ //===------------------------------------------------------------------===//
+ // Implementation for interface from ConstraintManager.
+ //===------------------------------------------------------------------===//
+
+ bool canReasonAbout(SVal X) const override;
+
+ ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override;
+
+ const llvm::APSInt *getSymVal(ProgramStateRef State,
+ SymbolRef Sym) const override;
+
+ ProgramStateRef removeDeadBindings(ProgramStateRef State,
+ SymbolReaper &SymReaper) override;
+
+ void print(ProgramStateRef State, raw_ostream &Out, const char *nl,
+ const char *sep) override;
+
+ //===------------------------------------------------------------------===//
+ // Implementation for interface from RangedConstraintManager.
+ //===------------------------------------------------------------------===//
ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym,
const llvm::APSInt &V,
@@ -313,26 +332,19 @@ public:
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) override;
- ProgramStateRef assumeSymbolWithinInclusiveRange(
+ ProgramStateRef assumeSymWithinInclusiveRange(
ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
const llvm::APSInt &To, const llvm::APSInt &Adjustment) override;
- ProgramStateRef assumeSymbolOutOfInclusiveRange(
+ ProgramStateRef assumeSymOutsideInclusiveRange(
ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
const llvm::APSInt &To, const llvm::APSInt &Adjustment) override;
- const llvm::APSInt *getSymVal(ProgramStateRef St,
- SymbolRef Sym) const override;
- ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override;
-
- ProgramStateRef removeDeadBindings(ProgramStateRef St,
- SymbolReaper &SymReaper) override;
-
- void print(ProgramStateRef St, raw_ostream &Out, const char *nl,
- const char *sep) override;
-
private:
RangeSet::Factory F;
+
+ RangeSet getRange(ProgramStateRef State, SymbolRef Sym);
+
RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment);
@@ -356,10 +368,46 @@ ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) {
return llvm::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder());
}
-const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St,
- SymbolRef Sym) const {
- const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(Sym);
- return T ? T->getConcreteValue() : nullptr;
+bool RangeConstraintManager::canReasonAbout(SVal X) const {
+ Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
+ if (SymVal && SymVal->isExpression()) {
+ const SymExpr *SE = SymVal->getSymbol();
+
+ if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) {
+ switch (SIE->getOpcode()) {
+ // We don't reason yet about bitwise-constraints on symbolic values.
+ case BO_And:
+ case BO_Or:
+ case BO_Xor:
+ return false;
+ // We don't reason yet about these arithmetic constraints on
+ // symbolic values.
+ case BO_Mul:
+ case BO_Div:
+ case BO_Rem:
+ case BO_Shl:
+ case BO_Shr:
+ return false;
+ // All other cases.
+ default:
+ return true;
+ }
+ }
+
+ if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(SE)) {
+ if (BinaryOperator::isComparisonOp(SSE->getOpcode())) {
+ // We handle Loc <> Loc comparisons, but not (yet) NonLoc <> NonLoc.
+ if (Loc::isLocType(SSE->getLHS()->getType())) {
+ assert(Loc::isLocType(SSE->getRHS()->getType()));
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ return true;
}
ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State,
@@ -386,6 +434,12 @@ ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State,
return ConditionTruthVal();
}
+const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St,
+ SymbolRef Sym) const {
+ const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(Sym);
+ return T ? T->getConcreteValue() : nullptr;
+}
+
/// Scan all symbols referenced by the constraints. If the symbol is not alive
/// as marked in LSymbols, mark it as dead in DSymbols.
ProgramStateRef
@@ -429,7 +483,7 @@ RangeSet RangeConstraintManager::getRange(ProgramStateRef State,
}
//===------------------------------------------------------------------------===
-// assumeSymX methods: public interface for RangeConstraintManager.
+// assumeSymX methods: protected interface for RangeConstraintManager.
//===------------------------------------------------------------------------===/
// The syntax for ranges below is mathematical, using [x, y] for closed ranges
@@ -646,7 +700,7 @@ RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym,
return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
}
-ProgramStateRef RangeConstraintManager::assumeSymbolWithinInclusiveRange(
+ProgramStateRef RangeConstraintManager::assumeSymWithinInclusiveRange(
ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
const llvm::APSInt &To, const llvm::APSInt &Adjustment) {
RangeSet New = getSymGERange(State, Sym, From, Adjustment);
@@ -656,7 +710,7 @@ ProgramStateRef RangeConstraintManager::assumeSymbolWithinInclusiveRange(
return New.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, New);
}
-ProgramStateRef RangeConstraintManager::assumeSymbolOutOfInclusiveRange(
+ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange(
ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
const llvm::APSInt &To, const llvm::APSInt &Adjustment) {
RangeSet RangeLT = getSymLTRange(State, Sym, From, Adjustment);
diff --git a/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
new file mode 100644
index 0000000000000..1304116f4974b
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
@@ -0,0 +1,204 @@
+//== RangedConstraintManager.cpp --------------------------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines RangedConstraintManager, a class that provides a
+// range-based constraint manager interface.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RangedConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+
+namespace clang {
+
+namespace ento {
+
+RangedConstraintManager::~RangedConstraintManager() {}
+
+ProgramStateRef RangedConstraintManager::assumeSym(ProgramStateRef State,
+ SymbolRef Sym,
+ bool Assumption) {
+ // Handle SymbolData.
+ if (isa<SymbolData>(Sym)) {
+ return assumeSymUnsupported(State, Sym, Assumption);
+
+ // Handle symbolic expression.
+ } else if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(Sym)) {
+ // We can only simplify expressions whose RHS is an integer.
+
+ BinaryOperator::Opcode op = SIE->getOpcode();
+ if (BinaryOperator::isComparisonOp(op)) {
+ if (!Assumption)
+ op = BinaryOperator::negateComparisonOp(op);
+
+ return assumeSymRel(State, SIE->getLHS(), op, SIE->getRHS());
+ }
+
+ } else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) {
+ // Translate "a != b" to "(b - a) != 0".
+ // We invert the order of the operands as a heuristic for how loop
+ // conditions are usually written ("begin != end") as compared to length
+ // calculations ("end - begin"). The more correct thing to do would be to
+ // canonicalize "a - b" and "b - a", which would allow us to treat
+ // "a != b" and "b != a" the same.
+ SymbolManager &SymMgr = getSymbolManager();
+ BinaryOperator::Opcode Op = SSE->getOpcode();
+ assert(BinaryOperator::isComparisonOp(Op));
+
+ // For now, we only support comparing pointers.
+ assert(Loc::isLocType(SSE->getLHS()->getType()));
+ assert(Loc::isLocType(SSE->getRHS()->getType()));
+ QualType DiffTy = SymMgr.getContext().getPointerDiffType();
+ SymbolRef Subtraction =
+ SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy);
+
+ const llvm::APSInt &Zero = getBasicVals().getValue(0, DiffTy);
+ Op = BinaryOperator::reverseComparisonOp(Op);
+ if (!Assumption)
+ Op = BinaryOperator::negateComparisonOp(Op);
+ return assumeSymRel(State, Subtraction, Op, Zero);
+ }
+
+ // If we get here, there's nothing else we can do but treat the symbol as
+ // opaque.
+ return assumeSymUnsupported(State, Sym, Assumption);
+}
+
+ProgramStateRef RangedConstraintManager::assumeSymInclusiveRange(
+ ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
+ const llvm::APSInt &To, bool InRange) {
+ // Get the type used for calculating wraparound.
+ BasicValueFactory &BVF = getBasicVals();
+ APSIntType WraparoundType = BVF.getAPSIntType(Sym->getType());
+
+ llvm::APSInt Adjustment = WraparoundType.getZeroValue();
+ SymbolRef AdjustedSym = Sym;
+ computeAdjustment(AdjustedSym, Adjustment);
+
+ // Convert the right-hand side integer as necessary.
+ APSIntType ComparisonType = std::max(WraparoundType, APSIntType(From));
+ llvm::APSInt ConvertedFrom = ComparisonType.convert(From);
+ llvm::APSInt ConvertedTo = ComparisonType.convert(To);
+
+ // Prefer unsigned comparisons.
+ if (ComparisonType.getBitWidth() == WraparoundType.getBitWidth() &&
+ ComparisonType.isUnsigned() && !WraparoundType.isUnsigned())
+ Adjustment.setIsSigned(false);
+
+ if (InRange)
+ return assumeSymWithinInclusiveRange(State, AdjustedSym, ConvertedFrom,
+ ConvertedTo, Adjustment);
+ return assumeSymOutsideInclusiveRange(State, AdjustedSym, ConvertedFrom,
+ ConvertedTo, Adjustment);
+}
+
+ProgramStateRef
+RangedConstraintManager::assumeSymUnsupported(ProgramStateRef State,
+ SymbolRef Sym, bool Assumption) {
+ BasicValueFactory &BVF = getBasicVals();
+ QualType T = Sym->getType();
+
+ // Non-integer types are not supported.
+ if (!T->isIntegralOrEnumerationType())
+ return State;
+
+ // Reverse the operation and add directly to state.
+ const llvm::APSInt &Zero = BVF.getValue(0, T);
+ if (Assumption)
+ return assumeSymNE(State, Sym, Zero, Zero);
+ else
+ return assumeSymEQ(State, Sym, Zero, Zero);
+}
+
+ProgramStateRef RangedConstraintManager::assumeSymRel(ProgramStateRef State,
+ SymbolRef Sym,
+ BinaryOperator::Opcode Op,
+ const llvm::APSInt &Int) {
+ assert(BinaryOperator::isComparisonOp(Op) &&
+ "Non-comparison ops should be rewritten as comparisons to zero.");
+
+ // Simplification: translate an assume of a constraint of the form
+ // "(exp comparison_op expr) != 0" to true into an assume of
+ // "exp comparison_op expr" to true. (And similarly, an assume of the form
+ // "(exp comparison_op expr) == 0" to true into an assume of
+ // "exp comparison_op expr" to false.)
+ if (Int == 0 && (Op == BO_EQ || Op == BO_NE)) {
+ if (const BinarySymExpr *SE = dyn_cast<BinarySymExpr>(Sym))
+ if (BinaryOperator::isComparisonOp(SE->getOpcode()))
+ return assumeSym(State, Sym, (Op == BO_NE ? true : false));
+ }
+
+ // Get the type used for calculating wraparound.
+ BasicValueFactory &BVF = getBasicVals();
+ APSIntType WraparoundType = BVF.getAPSIntType(Sym->getType());
+
+ // We only handle simple comparisons of the form "$sym == constant"
+ // or "($sym+constant1) == constant2".
+ // The adjustment is "constant1" in the above expression. It's used to
+ // "slide" the solution range around for modular arithmetic. For example,
+ // x < 4 has the solution [0, 3]. x+2 < 4 has the solution [0-2, 3-2], which
+ // in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to
+ // the subclasses of SimpleConstraintManager to handle the adjustment.
+ llvm::APSInt Adjustment = WraparoundType.getZeroValue();
+ computeAdjustment(Sym, Adjustment);
+
+ // Convert the right-hand side integer as necessary.
+ APSIntType ComparisonType = std::max(WraparoundType, APSIntType(Int));
+ llvm::APSInt ConvertedInt = ComparisonType.convert(Int);
+
+ // Prefer unsigned comparisons.
+ if (ComparisonType.getBitWidth() == WraparoundType.getBitWidth() &&
+ ComparisonType.isUnsigned() && !WraparoundType.isUnsigned())
+ Adjustment.setIsSigned(false);
+
+ switch (Op) {
+ default:
+ llvm_unreachable("invalid operation not caught by assertion above");
+
+ case BO_EQ:
+ return assumeSymEQ(State, Sym, ConvertedInt, Adjustment);
+
+ case BO_NE:
+ return assumeSymNE(State, Sym, ConvertedInt, Adjustment);
+
+ case BO_GT:
+ return assumeSymGT(State, Sym, ConvertedInt, Adjustment);
+
+ case BO_GE:
+ return assumeSymGE(State, Sym, ConvertedInt, Adjustment);
+
+ case BO_LT:
+ return assumeSymLT(State, Sym, ConvertedInt, Adjustment);
+
+ case BO_LE:
+ return assumeSymLE(State, Sym, ConvertedInt, Adjustment);
+ } // end switch
+}
+
+void RangedConstraintManager::computeAdjustment(SymbolRef &Sym,
+ llvm::APSInt &Adjustment) {
+ // Is it a "($sym+constant1)" expression?
+ if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(Sym)) {
+ BinaryOperator::Opcode Op = SE->getOpcode();
+ if (Op == BO_Add || Op == BO_Sub) {
+ Sym = SE->getLHS();
+ Adjustment = APSIntType(Adjustment).convert(SE->getRHS());
+
+ // Don't forget to negate the adjustment if it's being subtracted.
+ // This should happen /after/ promotion, in case the value being
+ // subtracted is, say, CHAR_MIN, and the promoted type is 'int'.
+ if (Op == BO_Sub)
+ Adjustment = -Adjustment;
+ }
+ }
+}
+
+} // end of namespace ento
+
+} // end of namespace clang
diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h b/lib/StaticAnalyzer/Core/RangedConstraintManager.h
index 1128e775b3205..a4e6062a4f576 100644
--- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.h
+++ b/lib/StaticAnalyzer/Core/RangedConstraintManager.h
@@ -1,4 +1,4 @@
-//== SimpleConstraintManager.h ----------------------------------*- C++ -*--==//
+//== RangedConstraintManager.h ----------------------------------*- C++ -*--==//
//
// The LLVM Compiler Infrastructure
//
@@ -7,59 +7,55 @@
//
//===----------------------------------------------------------------------===//
//
-// Code shared between BasicConstraintManager and RangeConstraintManager.
+// Ranged constraint manager, built on SimpleConstraintManager.
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_LIB_STATICANALYZER_CORE_SIMPLECONSTRAINTMANAGER_H
-#define LLVM_CLANG_LIB_STATICANALYZER_CORE_SIMPLECONSTRAINTMANAGER_H
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CORE_RANGEDCONSTRAINTMANAGER_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CORE_RANGEDCONSTRAINTMANAGER_H
-#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h"
namespace clang {
namespace ento {
-class SimpleConstraintManager : public ConstraintManager {
- SubEngine *SU;
- SValBuilder &SVB;
-
+class RangedConstraintManager : public SimpleConstraintManager {
public:
- SimpleConstraintManager(SubEngine *SE, SValBuilder &SB) : SU(SE), SVB(SB) {}
- ~SimpleConstraintManager() override;
+ RangedConstraintManager(SubEngine *SE, SValBuilder &SB)
+ : SimpleConstraintManager(SE, SB) {}
+
+ ~RangedConstraintManager() override;
//===------------------------------------------------------------------===//
- // Common implementation for the interface provided by ConstraintManager.
+ // Implementation for interface from SimpleConstraintManager.
//===------------------------------------------------------------------===//
- ProgramStateRef assume(ProgramStateRef State, DefinedSVal Cond,
- bool Assumption) override;
+ ProgramStateRef assumeSym(ProgramStateRef State, SymbolRef Sym,
+ bool Assumption) override;
- ProgramStateRef assume(ProgramStateRef State, NonLoc Cond, bool Assumption);
+ ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &From,
+ const llvm::APSInt &To,
+ bool InRange) override;
- ProgramStateRef assumeInclusiveRange(ProgramStateRef State, NonLoc Value,
- const llvm::APSInt &From,
- const llvm::APSInt &To,
- bool InRange) override;
+ ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym,
+ bool Assumption) override;
- ProgramStateRef assumeSymRel(ProgramStateRef State, const SymExpr *LHS,
- BinaryOperator::Opcode Op,
+protected:
+ /// Assume a constraint between a symbolic expression and a concrete integer.
+ virtual ProgramStateRef assumeSymRel(ProgramStateRef State, SymbolRef Sym,
+ BinaryOperator::Opcode op,
const llvm::APSInt &Int);
- ProgramStateRef assumeSymWithinInclusiveRange(ProgramStateRef State,
- SymbolRef Sym,
- const llvm::APSInt &From,
- const llvm::APSInt &To,
- bool InRange);
-
-protected:
//===------------------------------------------------------------------===//
// Interface that subclasses must implement.
//===------------------------------------------------------------------===//
// Each of these is of the form "$Sym+Adj <> V", where "<>" is the comparison
// operation for the method being invoked.
+
virtual ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym,
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) = 0;
@@ -84,28 +80,19 @@ protected:
const llvm::APSInt &V,
const llvm::APSInt &Adjustment) = 0;
- virtual ProgramStateRef assumeSymbolWithinInclusiveRange(
+ virtual ProgramStateRef assumeSymWithinInclusiveRange(
ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0;
- virtual ProgramStateRef assumeSymbolOutOfInclusiveRange(
+ virtual ProgramStateRef assumeSymOutsideInclusiveRange(
ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0;
//===------------------------------------------------------------------===//
// Internal implementation.
//===------------------------------------------------------------------===//
-
- BasicValueFactory &getBasicVals() const { return SVB.getBasicValueFactory(); }
- SymbolManager &getSymbolManager() const { return SVB.getSymbolManager(); }
-
- bool canReasonAbout(SVal X) const override;
-
- ProgramStateRef assumeAux(ProgramStateRef State, NonLoc Cond,
- bool Assumption);
-
- ProgramStateRef assumeAuxForSymbol(ProgramStateRef State, SymbolRef Sym,
- bool Assumption);
+private:
+ static void computeAdjustment(SymbolRef &Sym, llvm::APSInt &Adjustment);
};
} // end GR namespace
diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp
index 934cc5cd3ac46..dd7e9dd117815 100644
--- a/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -494,6 +494,11 @@ public: // Part of public interface to class.
return getBinding(getRegionBindings(S), L, T);
}
+ Optional<SVal> getDefaultBinding(Store S, const MemRegion *R) override {
+ RegionBindingsRef B = getRegionBindings(S);
+ return B.getDefaultBinding(R);
+ }
+
SVal getBinding(RegionBindingsConstRef B, Loc L, QualType T = QualType());
SVal getBindingForElement(RegionBindingsConstRef B, const ElementRegion *R);
@@ -1336,7 +1341,8 @@ SVal RegionStoreManager::ArrayToPointer(Loc Array, QualType T) {
if (!Array.getAs<loc::MemRegionVal>())
return UnknownVal();
- const MemRegion* R = Array.castAs<loc::MemRegionVal>().getRegion();
+ const SubRegion *R =
+ cast<SubRegion>(Array.castAs<loc::MemRegionVal>().getRegion());
NonLoc ZeroIdx = svalBuilder.makeZeroArrayIndex();
return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, R, Ctx));
}
@@ -1379,7 +1385,7 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T)
T = SR->getSymbol()->getType();
}
}
- MR = GetElementZeroRegion(MR, T);
+ MR = GetElementZeroRegion(cast<SubRegion>(MR), T);
}
// FIXME: Perhaps this method should just take a 'const MemRegion*' argument
diff --git a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
index 0e512ff808617..adb40178f5b16 100644
--- a/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
@@ -7,12 +7,12 @@
//
//===----------------------------------------------------------------------===//
//
-// This file defines SimpleConstraintManager, a class that holds code shared
-// between BasicConstraintManager and RangeConstraintManager.
+// This file defines SimpleConstraintManager, a class that provides a
+// simplified constraint manager interface, compared to ConstraintManager.
//
//===----------------------------------------------------------------------===//
-#include "SimpleConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
@@ -23,48 +23,6 @@ namespace ento {
SimpleConstraintManager::~SimpleConstraintManager() {}
-bool SimpleConstraintManager::canReasonAbout(SVal X) const {
- Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
- if (SymVal && SymVal->isExpression()) {
- const SymExpr *SE = SymVal->getSymbol();
-
- if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) {
- switch (SIE->getOpcode()) {
- // We don't reason yet about bitwise-constraints on symbolic values.
- case BO_And:
- case BO_Or:
- case BO_Xor:
- return false;
- // We don't reason yet about these arithmetic constraints on
- // symbolic values.
- case BO_Mul:
- case BO_Div:
- case BO_Rem:
- case BO_Shl:
- case BO_Shr:
- return false;
- // All other cases.
- default:
- return true;
- }
- }
-
- if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(SE)) {
- if (BinaryOperator::isComparisonOp(SSE->getOpcode())) {
- // We handle Loc <> Loc comparisons, but not (yet) NonLoc <> NonLoc.
- if (Loc::isLocType(SSE->getLHS()->getType())) {
- assert(Loc::isLocType(SSE->getRHS()->getType()));
- return true;
- }
- }
- }
-
- return false;
- }
-
- return true;
-}
-
ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State,
DefinedSVal Cond,
bool Assumption) {
@@ -92,23 +50,6 @@ ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State,
return State;
}
-ProgramStateRef
-SimpleConstraintManager::assumeAuxForSymbol(ProgramStateRef State,
- SymbolRef Sym, bool Assumption) {
- BasicValueFactory &BVF = getBasicVals();
- QualType T = Sym->getType();
-
- // None of the constraint solvers currently support non-integer types.
- if (!T->isIntegralOrEnumerationType())
- return State;
-
- const llvm::APSInt &zero = BVF.getValue(0, T);
- if (Assumption)
- return assumeSymNE(State, Sym, zero, zero);
- else
- return assumeSymEQ(State, Sym, zero, zero);
-}
-
ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State,
NonLoc Cond,
bool Assumption) {
@@ -118,7 +59,8 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State,
if (!canReasonAbout(Cond)) {
// Just add the constraint to the expression without trying to simplify.
SymbolRef Sym = Cond.getAsSymExpr();
- return assumeAuxForSymbol(State, Sym, Assumption);
+ assert(Sym);
+ return assumeSymUnsupported(State, Sym, Assumption);
}
switch (Cond.getSubKind()) {
@@ -129,51 +71,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State,
nonloc::SymbolVal SV = Cond.castAs<nonloc::SymbolVal>();
SymbolRef Sym = SV.getSymbol();
assert(Sym);
-
- // Handle SymbolData.
- if (!SV.isExpression()) {
- return assumeAuxForSymbol(State, Sym, Assumption);
-
- // Handle symbolic expression.
- } else if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(Sym)) {
- // We can only simplify expressions whose RHS is an integer.
-
- BinaryOperator::Opcode Op = SE->getOpcode();
- if (BinaryOperator::isComparisonOp(Op)) {
- if (!Assumption)
- Op = BinaryOperator::negateComparisonOp(Op);
-
- return assumeSymRel(State, SE->getLHS(), Op, SE->getRHS());
- }
-
- } else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) {
- // Translate "a != b" to "(b - a) != 0".
- // We invert the order of the operands as a heuristic for how loop
- // conditions are usually written ("begin != end") as compared to length
- // calculations ("end - begin"). The more correct thing to do would be to
- // canonicalize "a - b" and "b - a", which would allow us to treat
- // "a != b" and "b != a" the same.
- SymbolManager &SymMgr = getSymbolManager();
- BinaryOperator::Opcode Op = SSE->getOpcode();
- assert(BinaryOperator::isComparisonOp(Op));
-
- // For now, we only support comparing pointers.
- assert(Loc::isLocType(SSE->getLHS()->getType()));
- assert(Loc::isLocType(SSE->getRHS()->getType()));
- QualType DiffTy = SymMgr.getContext().getPointerDiffType();
- SymbolRef Subtraction =
- SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy);
-
- const llvm::APSInt &Zero = getBasicVals().getValue(0, DiffTy);
- Op = BinaryOperator::reverseComparisonOp(Op);
- if (!Assumption)
- Op = BinaryOperator::negateComparisonOp(Op);
- return assumeSymRel(State, Subtraction, Op, Zero);
- }
-
- // If we get here, there's nothing else we can do but treat the symbol as
- // opaque.
- return assumeAuxForSymbol(State, Sym, Assumption);
+ return assumeSym(State, Sym, Assumption);
}
case nonloc::ConcreteIntKind: {
@@ -206,7 +104,7 @@ ProgramStateRef SimpleConstraintManager::assumeInclusiveRange(
// Just add the constraint to the expression without trying to simplify.
SymbolRef Sym = Value.getAsSymExpr();
assert(Sym);
- return assumeSymWithinInclusiveRange(State, Sym, From, To, InRange);
+ return assumeSymInclusiveRange(State, Sym, From, To, InRange);
}
switch (Value.getSubKind()) {
@@ -217,7 +115,7 @@ ProgramStateRef SimpleConstraintManager::assumeInclusiveRange(
case nonloc::LocAsIntegerKind:
case nonloc::SymbolValKind: {
if (SymbolRef Sym = Value.getAsSymbol())
- return assumeSymWithinInclusiveRange(State, Sym, From, To, InRange);
+ return assumeSymInclusiveRange(State, Sym, From, To, InRange);
return State;
} // end switch
@@ -230,118 +128,6 @@ ProgramStateRef SimpleConstraintManager::assumeInclusiveRange(
} // end switch
}
-static void computeAdjustment(SymbolRef &Sym, llvm::APSInt &Adjustment) {
- // Is it a "($sym+constant1)" expression?
- if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(Sym)) {
- BinaryOperator::Opcode Op = SE->getOpcode();
- if (Op == BO_Add || Op == BO_Sub) {
- Sym = SE->getLHS();
- Adjustment = APSIntType(Adjustment).convert(SE->getRHS());
-
- // Don't forget to negate the adjustment if it's being subtracted.
- // This should happen /after/ promotion, in case the value being
- // subtracted is, say, CHAR_MIN, and the promoted type is 'int'.
- if (Op == BO_Sub)
- Adjustment = -Adjustment;
- }
- }
-}
-
-ProgramStateRef SimpleConstraintManager::assumeSymRel(ProgramStateRef State,
- const SymExpr *LHS,
- BinaryOperator::Opcode Op,
- const llvm::APSInt &Int) {
- assert(BinaryOperator::isComparisonOp(Op) &&
- "Non-comparison ops should be rewritten as comparisons to zero.");
-
- SymbolRef Sym = LHS;
-
- // Simplification: translate an assume of a constraint of the form
- // "(exp comparison_op expr) != 0" to true into an assume of
- // "exp comparison_op expr" to true. (And similarly, an assume of the form
- // "(exp comparison_op expr) == 0" to true into an assume of
- // "exp comparison_op expr" to false.)
- if (Int == 0 && (Op == BO_EQ || Op == BO_NE)) {
- if (const BinarySymExpr *SE = dyn_cast<BinarySymExpr>(Sym))
- if (BinaryOperator::isComparisonOp(SE->getOpcode()))
- return assume(State, nonloc::SymbolVal(Sym), (Op == BO_NE ? true : false));
- }
-
- // Get the type used for calculating wraparound.
- BasicValueFactory &BVF = getBasicVals();
- APSIntType WraparoundType = BVF.getAPSIntType(LHS->getType());
-
- // We only handle simple comparisons of the form "$sym == constant"
- // or "($sym+constant1) == constant2".
- // The adjustment is "constant1" in the above expression. It's used to
- // "slide" the solution range around for modular arithmetic. For example,
- // x < 4 has the solution [0, 3]. x+2 < 4 has the solution [0-2, 3-2], which
- // in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to
- // the subclasses of SimpleConstraintManager to handle the adjustment.
- llvm::APSInt Adjustment = WraparoundType.getZeroValue();
- computeAdjustment(Sym, Adjustment);
-
- // Convert the right-hand side integer as necessary.
- APSIntType ComparisonType = std::max(WraparoundType, APSIntType(Int));
- llvm::APSInt ConvertedInt = ComparisonType.convert(Int);
-
- // Prefer unsigned comparisons.
- if (ComparisonType.getBitWidth() == WraparoundType.getBitWidth() &&
- ComparisonType.isUnsigned() && !WraparoundType.isUnsigned())
- Adjustment.setIsSigned(false);
-
- switch (Op) {
- default:
- llvm_unreachable("invalid operation not caught by assertion above");
-
- case BO_EQ:
- return assumeSymEQ(State, Sym, ConvertedInt, Adjustment);
-
- case BO_NE:
- return assumeSymNE(State, Sym, ConvertedInt, Adjustment);
-
- case BO_GT:
- return assumeSymGT(State, Sym, ConvertedInt, Adjustment);
-
- case BO_GE:
- return assumeSymGE(State, Sym, ConvertedInt, Adjustment);
-
- case BO_LT:
- return assumeSymLT(State, Sym, ConvertedInt, Adjustment);
-
- case BO_LE:
- return assumeSymLE(State, Sym, ConvertedInt, Adjustment);
- } // end switch
-}
-
-ProgramStateRef SimpleConstraintManager::assumeSymWithinInclusiveRange(
- ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
- const llvm::APSInt &To, bool InRange) {
- // Get the type used for calculating wraparound.
- BasicValueFactory &BVF = getBasicVals();
- APSIntType WraparoundType = BVF.getAPSIntType(Sym->getType());
-
- llvm::APSInt Adjustment = WraparoundType.getZeroValue();
- SymbolRef AdjustedSym = Sym;
- computeAdjustment(AdjustedSym, Adjustment);
-
- // Convert the right-hand side integer as necessary.
- APSIntType ComparisonType = std::max(WraparoundType, APSIntType(From));
- llvm::APSInt ConvertedFrom = ComparisonType.convert(From);
- llvm::APSInt ConvertedTo = ComparisonType.convert(To);
-
- // Prefer unsigned comparisons.
- if (ComparisonType.getBitWidth() == WraparoundType.getBitWidth() &&
- ComparisonType.isUnsigned() && !WraparoundType.isUnsigned())
- Adjustment.setIsSigned(false);
-
- if (InRange)
- return assumeSymbolWithinInclusiveRange(State, AdjustedSym, ConvertedFrom,
- ConvertedTo, Adjustment);
- return assumeSymbolOutOfInclusiveRange(State, AdjustedSym, ConvertedFrom,
- ConvertedTo, Adjustment);
-}
-
} // end of namespace ento
} // end of namespace clang
diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index 28b43dd566d5c..82ce8b45fe781 100644
--- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -14,6 +14,7 @@
#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/SValVisitor.h"
using namespace clang;
using namespace ento;
@@ -44,6 +45,10 @@ public:
/// (integer) value, that value is returned. Otherwise, returns NULL.
const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal V) override;
+ /// Recursively descends into symbolic expressions and replaces symbols
+ /// with their known values (in the sense of the getKnownValue() method).
+ SVal simplifySVal(ProgramStateRef State, SVal V) override;
+
SVal MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op,
const llvm::APSInt &RHS, QualType resultTy);
};
@@ -362,6 +367,9 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
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<nonloc::ConcreteInt>().getValue();
BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i);
return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy);
@@ -534,11 +542,12 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
// Does the symbolic expression simplify to a constant?
// If so, "fold" the constant by setting 'lhs' to a ConcreteInt
// and try again.
- ConstraintManager &CMgr = state->getConstraintManager();
- if (const llvm::APSInt *Constant = CMgr.getSymVal(state, Sym)) {
- lhs = nonloc::ConcreteInt(*Constant);
- continue;
- }
+ SVal simplifiedLhs = simplifySVal(state, lhs);
+ if (simplifiedLhs != lhs)
+ if (auto simplifiedLhsAsNonLoc = simplifiedLhs.getAs<NonLoc>()) {
+ lhs = *simplifiedLhsAsNonLoc;
+ continue;
+ }
// Is the RHS a constant?
if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs))
@@ -941,20 +950,26 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
if (const MemRegion *region = lhs.getAsRegion()) {
rhs = convertToArrayIndex(rhs).castAs<NonLoc>();
SVal index = UnknownVal();
- const MemRegion *superR = nullptr;
+ const SubRegion *superR = nullptr;
+ // We need to know the type of the pointer in order to add an integer to it.
+ // Depending on the type, different amount of bytes is added.
QualType elementType;
if (const ElementRegion *elemReg = dyn_cast<ElementRegion>(region)) {
assert(op == BO_Add || op == BO_Sub);
index = evalBinOpNN(state, op, elemReg->getIndex(), rhs,
getArrayIndexType());
- superR = elemReg->getSuperRegion();
+ superR = cast<SubRegion>(elemReg->getSuperRegion());
elementType = elemReg->getElementType();
}
else if (isa<SubRegion>(region)) {
assert(op == BO_Add || op == BO_Sub);
index = (op == BO_Add) ? rhs : evalMinus(rhs);
- superR = region;
+ superR = cast<SubRegion>(region);
+ // TODO: Is this actually reliable? Maybe improving our MemRegion
+ // hierarchy to provide typed regions for all non-void pointers would be
+ // better. For instance, we cannot extend this towards LocAsInteger
+ // operations, where result type of the expression is integer.
if (resultTy->isAnyPointerType())
elementType = resultTy->getPointeeType();
}
@@ -984,3 +999,74 @@ const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state,
// FIXME: Add support for SymExprs.
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
+ // be possible, such as constant-folding an index in an ElementRegion.
+
+ class Simplifier : public FullSValVisitor<Simplifier, SVal> {
+ ProgramStateRef State;
+ SValBuilder &SVB;
+
+ public:
+ Simplifier(ProgramStateRef State)
+ : 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);
+ return nonloc::SymbolVal(S);
+ }
+
+ // TODO: Support SymbolCast. Support IntSymExpr when/if we actually
+ // start producing them.
+
+ SVal VisitSymIntExpr(const SymIntExpr *S) {
+ SVal LHS = Visit(S->getLHS());
+ SVal RHS;
+ // By looking at the APSInt in the right-hand side of S, we cannot
+ // figure out if it should be treated as a Loc or as a NonLoc.
+ // So make our guess by recalling that we cannot multiply pointers
+ // or compare a pointer to an integer.
+ if (Loc::isLocType(S->getLHS()->getType()) &&
+ BinaryOperator::isComparisonOp(S->getOpcode())) {
+ // The usual conversion of $sym to &SymRegion{$sym}, as they have
+ // the same meaning for Loc-type symbols, but the latter form
+ // is preferred in SVal computations for being Loc itself.
+ if (SymbolRef Sym = LHS.getAsSymbol()) {
+ assert(Loc::isLocType(Sym->getType()));
+ LHS = SVB.makeLoc(Sym);
+ }
+ RHS = SVB.makeIntLocVal(S->getRHS());
+ } else {
+ RHS = SVB.makeIntVal(S->getRHS());
+ }
+ return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
+ }
+
+ SVal VisitSymSymExpr(const SymSymExpr *S) {
+ SVal LHS = Visit(S->getLHS());
+ SVal RHS = Visit(S->getRHS());
+ return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
+ }
+
+ SVal VisitSymExpr(SymbolRef S) { return nonloc::SymbolVal(S); }
+
+ SVal VisitMemRegion(const MemRegion *R) { return loc::MemRegionVal(R); }
+
+ SVal VisitNonLocSymbolVal(nonloc::SymbolVal V) {
+ // Simplification is much more costly than computing complexity.
+ // For high complexity, it may be not worth it.
+ if (V.getSymbol()->computeComplexity() > 100)
+ return V;
+ return Visit(V.getSymbol());
+ }
+
+ SVal VisitSVal(SVal V) { return V; }
+ };
+
+ return Simplifier(State).Visit(V);
+}
diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp
index aca6e3b6255b8..ba48a60d5a1cc 100644
--- a/lib/StaticAnalyzer/Core/Store.cpp
+++ b/lib/StaticAnalyzer/Core/Store.cpp
@@ -42,8 +42,9 @@ StoreRef StoreManager::enterStackFrame(Store OldStore,
return Store;
}
-const MemRegion *StoreManager::MakeElementRegion(const MemRegion *Base,
- QualType EleTy, uint64_t index) {
+const ElementRegion *StoreManager::MakeElementRegion(const SubRegion *Base,
+ QualType EleTy,
+ uint64_t index) {
NonLoc idx = svalBuilder.makeArrayIndex(index);
return MRMgr.getElementRegion(EleTy, idx, Base, svalBuilder.getContext());
}
@@ -52,7 +53,7 @@ StoreRef StoreManager::BindDefault(Store store, const MemRegion *R, SVal V) {
return StoreRef(store, *this);
}
-const ElementRegion *StoreManager::GetElementZeroRegion(const MemRegion *R,
+const ElementRegion *StoreManager::GetElementZeroRegion(const SubRegion *R,
QualType T) {
NonLoc idx = svalBuilder.makeZeroArrayIndex();
assert(!T.isNull());
@@ -126,7 +127,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
case MemRegion::VarRegionKind:
case MemRegion::CXXTempObjectRegionKind:
case MemRegion::CXXBaseObjectRegionKind:
- return MakeElementRegion(R, PointeeTy);
+ return MakeElementRegion(cast<SubRegion>(R), PointeeTy);
case MemRegion::ElementRegionKind: {
// If we are casting from an ElementRegion to another type, the
@@ -171,7 +172,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
}
// Otherwise, create a new ElementRegion at offset 0.
- return MakeElementRegion(baseR, PointeeTy);
+ return MakeElementRegion(cast<SubRegion>(baseR), PointeeTy);
}
// We have a non-zero offset from the base region. We want to determine
@@ -202,10 +203,11 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
if (!newSuperR) {
// Create an intermediate ElementRegion to represent the raw byte.
// This will be the super region of the final ElementRegion.
- newSuperR = MakeElementRegion(baseR, Ctx.CharTy, off.getQuantity());
+ newSuperR = MakeElementRegion(cast<SubRegion>(baseR), Ctx.CharTy,
+ off.getQuantity());
}
- return MakeElementRegion(newSuperR, PointeeTy, newIndex);
+ return MakeElementRegion(cast<SubRegion>(newSuperR), PointeeTy, newIndex);
}
}
@@ -271,9 +273,8 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType,
BaseDecl = BaseType->getAsCXXRecordDecl();
assert(BaseDecl && "not a C++ object?");
- const MemRegion *BaseReg =
- MRMgr.getCXXBaseObjectRegion(BaseDecl, DerivedRegVal->getRegion(),
- IsVirtual);
+ const MemRegion *BaseReg = MRMgr.getCXXBaseObjectRegion(
+ BaseDecl, cast<SubRegion>(DerivedRegVal->getRegion()), IsVirtual);
return loc::MemRegionVal(BaseReg);
}
@@ -390,11 +391,11 @@ SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) {
return Base;
Loc BaseL = Base.castAs<Loc>();
- const MemRegion* BaseR = nullptr;
+ const SubRegion* BaseR = nullptr;
switch (BaseL.getSubKind()) {
case loc::MemRegionValKind:
- BaseR = BaseL.castAs<loc::MemRegionVal>().getRegion();
+ BaseR = cast<SubRegion>(BaseL.castAs<loc::MemRegionVal>().getRegion());
break;
case loc::GotoLabelKind:
@@ -434,7 +435,8 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
if (Base.isUnknownOrUndef() || Base.getAs<loc::ConcreteInt>())
return Base;
- const MemRegion* BaseRegion = Base.castAs<loc::MemRegionVal>().getRegion();
+ const SubRegion *BaseRegion =
+ Base.castAs<loc::MemRegionVal>().getRegionAs<SubRegion>();
// Pointer of any type can be cast and used as array base.
const ElementRegion *ElemR = dyn_cast<ElementRegion>(BaseRegion);
@@ -471,9 +473,8 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
if (isa<ElementRegion>(BaseRegion->StripCasts()))
return UnknownVal();
- return loc::MemRegionVal(MRMgr.getElementRegion(elementType, Offset,
- ElemR->getSuperRegion(),
- Ctx));
+ return loc::MemRegionVal(MRMgr.getElementRegion(
+ elementType, Offset, cast<SubRegion>(ElemR->getSuperRegion()), Ctx));
}
const llvm::APSInt& OffI = Offset.castAs<nonloc::ConcreteInt>().getValue();
@@ -484,7 +485,7 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
OffI));
// Construct the new ElementRegion.
- const MemRegion *ArrayR = ElemR->getSuperRegion();
+ const SubRegion *ArrayR = cast<SubRegion>(ElemR->getSuperRegion());
return loc::MemRegionVal(MRMgr.getElementRegion(elementType, NewIdx, ArrayR,
Ctx));
}
diff --git a/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp b/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
new file mode 100644
index 0000000000000..f9f9057a89cd7
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
@@ -0,0 +1,1618 @@
+//== Z3ConstraintManager.cpp --------------------------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/TargetInfo.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h"
+
+#include "clang/Config/config.h"
+
+using namespace clang;
+using namespace ento;
+
+#if CLANG_ANALYZER_WITH_Z3
+
+#include <z3.h>
+
+// Forward declarations
+namespace {
+class Z3Expr;
+class ConstraintZ3 {};
+} // end anonymous namespace
+
+typedef llvm::ImmutableSet<std::pair<SymbolRef, Z3Expr>> ConstraintZ3Ty;
+
+// Expansion of REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintZ3, Z3SetPair)
+namespace clang {
+namespace ento {
+template <>
+struct ProgramStateTrait<ConstraintZ3>
+ : public ProgramStatePartialTrait<ConstraintZ3Ty> {
+ static void *GDMIndex() {
+ static int Index;
+ return &Index;
+ }
+};
+} // end namespace ento
+} // end namespace clang
+
+namespace {
+
+class Z3Config {
+ friend class Z3Context;
+
+ Z3_config Config;
+
+public:
+ Z3Config() : Config(Z3_mk_config()) {
+ // Enable model finding
+ Z3_set_param_value(Config, "model", "true");
+ // Disable proof generation
+ Z3_set_param_value(Config, "proof", "false");
+ // Set timeout to 15000ms = 15s
+ Z3_set_param_value(Config, "timeout", "15000");
+ }
+
+ ~Z3Config() { Z3_del_config(Config); }
+}; // end class Z3Config
+
+class Z3Context {
+ Z3_context ZC_P;
+
+public:
+ static Z3_context ZC;
+
+ Z3Context() : ZC_P(Z3_mk_context_rc(Z3Config().Config)) { ZC = ZC_P; }
+
+ ~Z3Context() {
+ Z3_del_context(ZC);
+ Z3_finalize_memory();
+ ZC_P = nullptr;
+ }
+}; // end class Z3Context
+
+class Z3Sort {
+ friend class Z3Expr;
+
+ Z3_sort Sort;
+
+ Z3Sort() : Sort(nullptr) {}
+ Z3Sort(Z3_sort ZS) : Sort(ZS) {
+ Z3_inc_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort));
+ }
+
+public:
+ /// Override implicit copy constructor for correct reference counting.
+ Z3Sort(const Z3Sort &Copy) : Sort(Copy.Sort) {
+ Z3_inc_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort));
+ }
+
+ /// Provide move constructor
+ Z3Sort(Z3Sort &&Move) : Sort(nullptr) { *this = std::move(Move); }
+
+ /// Provide move assignment constructor
+ Z3Sort &operator=(Z3Sort &&Move) {
+ if (this != &Move) {
+ if (Sort)
+ Z3_dec_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort));
+ Sort = Move.Sort;
+ Move.Sort = nullptr;
+ }
+ return *this;
+ }
+
+ ~Z3Sort() {
+ if (Sort)
+ Z3_dec_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort));
+ }
+
+ // Return a boolean sort.
+ static Z3Sort getBoolSort() { return Z3Sort(Z3_mk_bool_sort(Z3Context::ZC)); }
+
+ // Return an appropriate bitvector sort for the given bitwidth.
+ static Z3Sort getBitvectorSort(unsigned BitWidth) {
+ return Z3Sort(Z3_mk_bv_sort(Z3Context::ZC, BitWidth));
+ }
+
+ // Return an appropriate floating-point sort for the given bitwidth.
+ static Z3Sort getFloatSort(unsigned BitWidth) {
+ Z3_sort Sort;
+
+ switch (BitWidth) {
+ default:
+ llvm_unreachable("Unsupported floating-point bitwidth!");
+ break;
+ case 16:
+ Sort = Z3_mk_fpa_sort_16(Z3Context::ZC);
+ break;
+ case 32:
+ Sort = Z3_mk_fpa_sort_32(Z3Context::ZC);
+ break;
+ case 64:
+ Sort = Z3_mk_fpa_sort_64(Z3Context::ZC);
+ break;
+ case 128:
+ Sort = Z3_mk_fpa_sort_128(Z3Context::ZC);
+ break;
+ }
+ return Z3Sort(Sort);
+ }
+
+ // Return an appropriate sort for the given AST.
+ static Z3Sort getSort(Z3_ast AST) {
+ return Z3Sort(Z3_get_sort(Z3Context::ZC, AST));
+ }
+
+ Z3_sort_kind getSortKind() const {
+ return Z3_get_sort_kind(Z3Context::ZC, Sort);
+ }
+
+ unsigned getBitvectorSortSize() const {
+ assert(getSortKind() == Z3_BV_SORT && "Not a bitvector sort!");
+ return Z3_get_bv_sort_size(Z3Context::ZC, Sort);
+ }
+
+ unsigned getFloatSortSize() const {
+ assert(getSortKind() == Z3_FLOATING_POINT_SORT &&
+ "Not a floating-point sort!");
+ return Z3_fpa_get_ebits(Z3Context::ZC, Sort) +
+ Z3_fpa_get_sbits(Z3Context::ZC, Sort);
+ }
+
+ bool operator==(const Z3Sort &Other) const {
+ return Z3_is_eq_sort(Z3Context::ZC, Sort, Other.Sort);
+ }
+
+ Z3Sort &operator=(const Z3Sort &Move) {
+ Z3_inc_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Move.Sort));
+ Z3_dec_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort));
+ Sort = Move.Sort;
+ return *this;
+ }
+
+ void print(raw_ostream &OS) const {
+ OS << Z3_sort_to_string(Z3Context::ZC, Sort);
+ }
+
+ LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); }
+}; // end class Z3Sort
+
+class Z3Expr {
+ friend class Z3Model;
+ friend class Z3Solver;
+
+ Z3_ast AST;
+
+ Z3Expr(Z3_ast ZA) : AST(ZA) { Z3_inc_ref(Z3Context::ZC, AST); }
+
+ // Return an appropriate floating-point rounding mode.
+ static Z3Expr getFloatRoundingMode() {
+ // TODO: Don't assume nearest ties to even rounding mode
+ return Z3Expr(Z3_mk_fpa_rne(Z3Context::ZC));
+ }
+
+ // Determine whether two float semantics are equivalent
+ static bool areEquivalent(const llvm::fltSemantics &LHS,
+ const llvm::fltSemantics &RHS) {
+ return (llvm::APFloat::semanticsPrecision(LHS) ==
+ llvm::APFloat::semanticsPrecision(RHS)) &&
+ (llvm::APFloat::semanticsMinExponent(LHS) ==
+ llvm::APFloat::semanticsMinExponent(RHS)) &&
+ (llvm::APFloat::semanticsMaxExponent(LHS) ==
+ llvm::APFloat::semanticsMaxExponent(RHS)) &&
+ (llvm::APFloat::semanticsSizeInBits(LHS) ==
+ llvm::APFloat::semanticsSizeInBits(RHS));
+ }
+
+public:
+ /// Override implicit copy constructor for correct reference counting.
+ Z3Expr(const Z3Expr &Copy) : AST(Copy.AST) { Z3_inc_ref(Z3Context::ZC, AST); }
+
+ /// Provide move constructor
+ Z3Expr(Z3Expr &&Move) : AST(nullptr) { *this = std::move(Move); }
+
+ /// Provide move assignment constructor
+ Z3Expr &operator=(Z3Expr &&Move) {
+ if (this != &Move) {
+ if (AST)
+ Z3_dec_ref(Z3Context::ZC, AST);
+ AST = Move.AST;
+ Move.AST = nullptr;
+ }
+ return *this;
+ }
+
+ ~Z3Expr() {
+ if (AST)
+ Z3_dec_ref(Z3Context::ZC, AST);
+ }
+
+ /// Get the corresponding IEEE floating-point type for a given bitwidth.
+ static const llvm::fltSemantics &getFloatSemantics(unsigned BitWidth) {
+ switch (BitWidth) {
+ default:
+ llvm_unreachable("Unsupported floating-point semantics!");
+ break;
+ case 16:
+ return llvm::APFloat::IEEEhalf();
+ case 32:
+ return llvm::APFloat::IEEEsingle();
+ case 64:
+ return llvm::APFloat::IEEEdouble();
+ case 128:
+ return llvm::APFloat::IEEEquad();
+ }
+ }
+
+ /// Construct a Z3Expr from a unary operator, given a Z3_context.
+ static Z3Expr fromUnOp(const UnaryOperator::Opcode Op, const Z3Expr &Exp) {
+ Z3_ast AST;
+
+ switch (Op) {
+ default:
+ llvm_unreachable("Unimplemented opcode");
+ break;
+
+ case UO_Minus:
+ AST = Z3_mk_bvneg(Z3Context::ZC, Exp.AST);
+ break;
+
+ case UO_Not:
+ AST = Z3_mk_bvnot(Z3Context::ZC, Exp.AST);
+ break;
+
+ case UO_LNot:
+ AST = Z3_mk_not(Z3Context::ZC, Exp.AST);
+ break;
+ }
+
+ return Z3Expr(AST);
+ }
+
+ /// Construct a Z3Expr from a floating-point unary operator, given a
+ /// Z3_context.
+ static Z3Expr fromFloatUnOp(const UnaryOperator::Opcode Op,
+ const Z3Expr &Exp) {
+ Z3_ast AST;
+
+ switch (Op) {
+ default:
+ llvm_unreachable("Unimplemented opcode");
+ break;
+
+ case UO_Minus:
+ AST = Z3_mk_fpa_neg(Z3Context::ZC, Exp.AST);
+ break;
+
+ case UO_LNot:
+ return Z3Expr::fromUnOp(Op, Exp);
+ }
+
+ return Z3Expr(AST);
+ }
+
+ /// Construct a Z3Expr from a n-ary binary operator.
+ static Z3Expr fromNBinOp(const BinaryOperator::Opcode Op,
+ const std::vector<Z3_ast> &ASTs) {
+ Z3_ast AST;
+
+ switch (Op) {
+ default:
+ llvm_unreachable("Unimplemented opcode");
+ break;
+
+ case BO_LAnd:
+ AST = Z3_mk_and(Z3Context::ZC, ASTs.size(), ASTs.data());
+ break;
+
+ case BO_LOr:
+ AST = Z3_mk_or(Z3Context::ZC, ASTs.size(), ASTs.data());
+ break;
+ }
+
+ return Z3Expr(AST);
+ }
+
+ /// Construct a Z3Expr from a binary operator, given a Z3_context.
+ static Z3Expr fromBinOp(const Z3Expr &LHS, const BinaryOperator::Opcode Op,
+ const Z3Expr &RHS, bool isSigned) {
+ Z3_ast AST;
+
+ assert(Z3Sort::getSort(LHS.AST) == Z3Sort::getSort(RHS.AST) &&
+ "AST's must have the same sort!");
+
+ switch (Op) {
+ default:
+ llvm_unreachable("Unimplemented opcode");
+ break;
+
+ // Multiplicative operators
+ case BO_Mul:
+ AST = Z3_mk_bvmul(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+ case BO_Div:
+ AST = isSigned ? Z3_mk_bvsdiv(Z3Context::ZC, LHS.AST, RHS.AST)
+ : Z3_mk_bvudiv(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+ case BO_Rem:
+ AST = isSigned ? Z3_mk_bvsrem(Z3Context::ZC, LHS.AST, RHS.AST)
+ : Z3_mk_bvurem(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+
+ // Additive operators
+ case BO_Add:
+ AST = Z3_mk_bvadd(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+ case BO_Sub:
+ AST = Z3_mk_bvsub(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+
+ // Bitwise shift operators
+ case BO_Shl:
+ AST = Z3_mk_bvshl(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+ case BO_Shr:
+ AST = isSigned ? Z3_mk_bvashr(Z3Context::ZC, LHS.AST, RHS.AST)
+ : Z3_mk_bvlshr(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+
+ // Relational operators
+ case BO_LT:
+ AST = isSigned ? Z3_mk_bvslt(Z3Context::ZC, LHS.AST, RHS.AST)
+ : Z3_mk_bvult(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+ case BO_GT:
+ AST = isSigned ? Z3_mk_bvsgt(Z3Context::ZC, LHS.AST, RHS.AST)
+ : Z3_mk_bvugt(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+ case BO_LE:
+ AST = isSigned ? Z3_mk_bvsle(Z3Context::ZC, LHS.AST, RHS.AST)
+ : Z3_mk_bvule(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+ case BO_GE:
+ AST = isSigned ? Z3_mk_bvsge(Z3Context::ZC, LHS.AST, RHS.AST)
+ : Z3_mk_bvuge(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+
+ // Equality operators
+ case BO_EQ:
+ AST = Z3_mk_eq(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+ case BO_NE:
+ return Z3Expr::fromUnOp(UO_LNot,
+ Z3Expr::fromBinOp(LHS, BO_EQ, RHS, isSigned));
+ break;
+
+ // Bitwise operators
+ case BO_And:
+ AST = Z3_mk_bvand(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+ case BO_Xor:
+ AST = Z3_mk_bvxor(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+ case BO_Or:
+ AST = Z3_mk_bvor(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+
+ // Logical operators
+ case BO_LAnd:
+ case BO_LOr: {
+ std::vector<Z3_ast> Args = {LHS.AST, RHS.AST};
+ return Z3Expr::fromNBinOp(Op, Args);
+ }
+ }
+
+ return Z3Expr(AST);
+ }
+
+ /// Construct a Z3Expr from a special floating-point binary operator, given
+ /// a Z3_context.
+ static Z3Expr fromFloatSpecialBinOp(const Z3Expr &LHS,
+ const BinaryOperator::Opcode Op,
+ const llvm::APFloat::fltCategory &RHS) {
+ Z3_ast AST;
+
+ switch (Op) {
+ default:
+ llvm_unreachable("Unimplemented opcode");
+ break;
+
+ // Equality operators
+ case BO_EQ:
+ switch (RHS) {
+ case llvm::APFloat::fcInfinity:
+ AST = Z3_mk_fpa_is_infinite(Z3Context::ZC, LHS.AST);
+ break;
+ case llvm::APFloat::fcNaN:
+ AST = Z3_mk_fpa_is_nan(Z3Context::ZC, LHS.AST);
+ break;
+ case llvm::APFloat::fcNormal:
+ AST = Z3_mk_fpa_is_normal(Z3Context::ZC, LHS.AST);
+ break;
+ case llvm::APFloat::fcZero:
+ AST = Z3_mk_fpa_is_zero(Z3Context::ZC, LHS.AST);
+ break;
+ }
+ break;
+ case BO_NE:
+ return Z3Expr::fromFloatUnOp(
+ UO_LNot, Z3Expr::fromFloatSpecialBinOp(LHS, BO_EQ, RHS));
+ break;
+ }
+
+ return Z3Expr(AST);
+ }
+
+ /// Construct a Z3Expr from a floating-point binary operator, given a
+ /// Z3_context.
+ static Z3Expr fromFloatBinOp(const Z3Expr &LHS,
+ const BinaryOperator::Opcode Op,
+ const Z3Expr &RHS) {
+ Z3_ast AST;
+
+ assert(Z3Sort::getSort(LHS.AST) == Z3Sort::getSort(RHS.AST) &&
+ "AST's must have the same sort!");
+
+ switch (Op) {
+ default:
+ llvm_unreachable("Unimplemented opcode");
+ break;
+
+ // Multiplicative operators
+ case BO_Mul: {
+ Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode();
+ AST = Z3_mk_fpa_mul(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST);
+ break;
+ }
+ case BO_Div: {
+ Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode();
+ AST = Z3_mk_fpa_div(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST);
+ break;
+ }
+ case BO_Rem:
+ AST = Z3_mk_fpa_rem(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+
+ // Additive operators
+ case BO_Add: {
+ Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode();
+ AST = Z3_mk_fpa_add(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST);
+ break;
+ }
+ case BO_Sub: {
+ Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode();
+ AST = Z3_mk_fpa_sub(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST);
+ break;
+ }
+
+ // Relational operators
+ case BO_LT:
+ AST = Z3_mk_fpa_lt(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+ case BO_GT:
+ AST = Z3_mk_fpa_gt(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+ case BO_LE:
+ AST = Z3_mk_fpa_leq(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+ case BO_GE:
+ AST = Z3_mk_fpa_geq(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+
+ // Equality operators
+ case BO_EQ:
+ AST = Z3_mk_fpa_eq(Z3Context::ZC, LHS.AST, RHS.AST);
+ break;
+ case BO_NE:
+ return Z3Expr::fromFloatUnOp(UO_LNot,
+ Z3Expr::fromFloatBinOp(LHS, BO_EQ, RHS));
+ break;
+
+ // Logical operators
+ case BO_LAnd:
+ case BO_LOr:
+ return Z3Expr::fromBinOp(LHS, Op, RHS, false);
+ }
+
+ return Z3Expr(AST);
+ }
+
+ /// Construct a Z3Expr from a SymbolData, given a Z3_context.
+ static Z3Expr fromData(const SymbolID ID, bool isBool, bool isFloat,
+ uint64_t BitWidth) {
+ llvm::Twine Name = "$" + llvm::Twine(ID);
+
+ Z3Sort Sort;
+ if (isBool)
+ Sort = Z3Sort::getBoolSort();
+ else if (isFloat)
+ Sort = Z3Sort::getFloatSort(BitWidth);
+ else
+ Sort = Z3Sort::getBitvectorSort(BitWidth);
+
+ Z3_symbol Symbol = Z3_mk_string_symbol(Z3Context::ZC, Name.str().c_str());
+ Z3_ast AST = Z3_mk_const(Z3Context::ZC, Symbol, Sort.Sort);
+ return Z3Expr(AST);
+ }
+
+ /// Construct a Z3Expr from a SymbolCast, given a Z3_context.
+ static Z3Expr fromCast(const Z3Expr &Exp, QualType ToTy, uint64_t ToBitWidth,
+ QualType FromTy, uint64_t FromBitWidth) {
+ Z3_ast AST;
+
+ if ((FromTy->isIntegralOrEnumerationType() &&
+ ToTy->isIntegralOrEnumerationType()) ||
+ (FromTy->isAnyPointerType() ^ ToTy->isAnyPointerType()) ||
+ (FromTy->isBlockPointerType() ^ ToTy->isBlockPointerType()) ||
+ (FromTy->isReferenceType() ^ ToTy->isReferenceType())) {
+ // Special case: Z3 boolean type is distinct from bitvector type, so
+ // must use if-then-else expression instead of direct cast
+ if (FromTy->isBooleanType()) {
+ assert(ToBitWidth > 0 && "BitWidth must be positive!");
+ Z3Expr Zero = Z3Expr::fromInt("0", ToBitWidth);
+ Z3Expr One = Z3Expr::fromInt("1", ToBitWidth);
+ AST = Z3_mk_ite(Z3Context::ZC, Exp.AST, One.AST, Zero.AST);
+ } else if (ToBitWidth > FromBitWidth) {
+ AST = FromTy->isSignedIntegerOrEnumerationType()
+ ? Z3_mk_sign_ext(Z3Context::ZC, ToBitWidth - FromBitWidth,
+ Exp.AST)
+ : Z3_mk_zero_ext(Z3Context::ZC, ToBitWidth - FromBitWidth,
+ Exp.AST);
+ } else if (ToBitWidth < FromBitWidth) {
+ AST = Z3_mk_extract(Z3Context::ZC, ToBitWidth - 1, 0, Exp.AST);
+ } else {
+ // Both are bitvectors with the same width, ignore the type cast
+ return Exp;
+ }
+ } else if (FromTy->isRealFloatingType() && ToTy->isRealFloatingType()) {
+ if (ToBitWidth != FromBitWidth) {
+ Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode();
+ Z3Sort Sort = Z3Sort::getFloatSort(ToBitWidth);
+ AST = Z3_mk_fpa_to_fp_float(Z3Context::ZC, RoundingMode.AST, Exp.AST,
+ Sort.Sort);
+ } else {
+ return Exp;
+ }
+ } else if (FromTy->isIntegralOrEnumerationType() &&
+ ToTy->isRealFloatingType()) {
+ Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode();
+ Z3Sort Sort = Z3Sort::getFloatSort(ToBitWidth);
+ AST = FromTy->isSignedIntegerOrEnumerationType()
+ ? Z3_mk_fpa_to_fp_signed(Z3Context::ZC, RoundingMode.AST,
+ Exp.AST, Sort.Sort)
+ : Z3_mk_fpa_to_fp_unsigned(Z3Context::ZC, RoundingMode.AST,
+ Exp.AST, Sort.Sort);
+ } else if (FromTy->isRealFloatingType() &&
+ ToTy->isIntegralOrEnumerationType()) {
+ Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode();
+ AST = ToTy->isSignedIntegerOrEnumerationType()
+ ? Z3_mk_fpa_to_sbv(Z3Context::ZC, RoundingMode.AST, Exp.AST,
+ ToBitWidth)
+ : Z3_mk_fpa_to_ubv(Z3Context::ZC, RoundingMode.AST, Exp.AST,
+ ToBitWidth);
+ } else {
+ llvm_unreachable("Unsupported explicit type cast!");
+ }
+
+ return Z3Expr(AST);
+ }
+
+ /// Construct a Z3Expr from a boolean, given a Z3_context.
+ static Z3Expr fromBoolean(const bool Bool) {
+ Z3_ast AST = Bool ? Z3_mk_true(Z3Context::ZC) : Z3_mk_false(Z3Context::ZC);
+ return Z3Expr(AST);
+ }
+
+ /// Construct a Z3Expr from a finite APFloat, given a Z3_context.
+ static Z3Expr fromAPFloat(const llvm::APFloat &Float) {
+ Z3_ast AST;
+ Z3Sort Sort = Z3Sort::getFloatSort(
+ llvm::APFloat::semanticsSizeInBits(Float.getSemantics()));
+
+ llvm::APSInt Int = llvm::APSInt(Float.bitcastToAPInt(), true);
+ Z3Expr Z3Int = Z3Expr::fromAPSInt(Int);
+ AST = Z3_mk_fpa_to_fp_bv(Z3Context::ZC, Z3Int.AST, Sort.Sort);
+
+ return Z3Expr(AST);
+ }
+
+ /// Construct a Z3Expr from an APSInt, given a Z3_context.
+ static Z3Expr fromAPSInt(const llvm::APSInt &Int) {
+ Z3Sort Sort = Z3Sort::getBitvectorSort(Int.getBitWidth());
+ Z3_ast AST =
+ Z3_mk_numeral(Z3Context::ZC, Int.toString(10).c_str(), Sort.Sort);
+ return Z3Expr(AST);
+ }
+
+ /// Construct a Z3Expr from an integer, given a Z3_context.
+ static Z3Expr fromInt(const char *Int, uint64_t BitWidth) {
+ Z3Sort Sort = Z3Sort::getBitvectorSort(BitWidth);
+ Z3_ast AST = Z3_mk_numeral(Z3Context::ZC, Int, Sort.Sort);
+ return Z3Expr(AST);
+ }
+
+ /// Construct an APFloat from a Z3Expr, given the AST representation
+ static bool toAPFloat(const Z3Sort &Sort, const Z3_ast &AST,
+ llvm::APFloat &Float, bool useSemantics = true) {
+ assert(Sort.getSortKind() == Z3_FLOATING_POINT_SORT &&
+ "Unsupported sort to floating-point!");
+
+ llvm::APSInt Int(Sort.getFloatSortSize(), true);
+ const llvm::fltSemantics &Semantics =
+ Z3Expr::getFloatSemantics(Sort.getFloatSortSize());
+ Z3Sort BVSort = Z3Sort::getBitvectorSort(Sort.getFloatSortSize());
+ if (!Z3Expr::toAPSInt(BVSort, AST, Int, true)) {
+ return false;
+ }
+
+ if (useSemantics &&
+ !Z3Expr::areEquivalent(Float.getSemantics(), Semantics)) {
+ assert(false && "Floating-point types don't match!");
+ return false;
+ }
+
+ Float = llvm::APFloat(Semantics, Int);
+ return true;
+ }
+
+ /// Construct an APSInt from a Z3Expr, given the AST representation
+ static bool toAPSInt(const Z3Sort &Sort, const Z3_ast &AST, llvm::APSInt &Int,
+ bool useSemantics = true) {
+ switch (Sort.getSortKind()) {
+ default:
+ llvm_unreachable("Unsupported sort to integer!");
+ case Z3_BV_SORT: {
+ if (useSemantics && Int.getBitWidth() != Sort.getBitvectorSortSize()) {
+ assert(false && "Bitvector types don't match!");
+ return false;
+ }
+
+ uint64_t Value[2];
+ // Force cast because Z3 defines __uint64 to be a unsigned long long
+ // type, which isn't compatible with a unsigned long type, even if they
+ // are the same size.
+ Z3_get_numeral_uint64(Z3Context::ZC, AST,
+ reinterpret_cast<__uint64 *>(&Value[0]));
+ if (Sort.getBitvectorSortSize() <= 64) {
+ Int = llvm::APSInt(llvm::APInt(Int.getBitWidth(), Value[0]), true);
+ } else if (Sort.getBitvectorSortSize() == 128) {
+ Z3Expr ASTHigh = Z3Expr(Z3_mk_extract(Z3Context::ZC, 127, 64, AST));
+ Z3_get_numeral_uint64(Z3Context::ZC, AST,
+ reinterpret_cast<__uint64 *>(&Value[1]));
+ Int = llvm::APSInt(llvm::APInt(Int.getBitWidth(), Value), true);
+ } else {
+ assert(false && "Bitwidth not supported!");
+ return false;
+ }
+ return true;
+ }
+ case Z3_BOOL_SORT:
+ if (useSemantics && Int.getBitWidth() < 1) {
+ assert(false && "Boolean type doesn't match!");
+ return false;
+ }
+ Int = llvm::APSInt(
+ llvm::APInt(Int.getBitWidth(),
+ Z3_get_bool_value(Z3Context::ZC, AST) == Z3_L_TRUE ? 1
+ : 0),
+ true);
+ return true;
+ }
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(Z3_get_ast_hash(Z3Context::ZC, AST));
+ }
+
+ bool operator<(const Z3Expr &Other) const {
+ llvm::FoldingSetNodeID ID1, ID2;
+ Profile(ID1);
+ Other.Profile(ID2);
+ return ID1 < ID2;
+ }
+
+ /// Comparison of AST equality, not model equivalence.
+ bool operator==(const Z3Expr &Other) const {
+ assert(Z3_is_eq_sort(Z3Context::ZC, Z3_get_sort(Z3Context::ZC, AST),
+ Z3_get_sort(Z3Context::ZC, Other.AST)) &&
+ "AST's must have the same sort");
+ return Z3_is_eq_ast(Z3Context::ZC, AST, Other.AST);
+ }
+
+ /// Override implicit move constructor for correct reference counting.
+ Z3Expr &operator=(const Z3Expr &Move) {
+ Z3_inc_ref(Z3Context::ZC, Move.AST);
+ Z3_dec_ref(Z3Context::ZC, AST);
+ AST = Move.AST;
+ return *this;
+ }
+
+ void print(raw_ostream &OS) const {
+ OS << Z3_ast_to_string(Z3Context::ZC, AST);
+ }
+
+ LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); }
+}; // end class Z3Expr
+
+class Z3Model {
+ Z3_model Model;
+
+public:
+ Z3Model(Z3_model ZM) : Model(ZM) { Z3_model_inc_ref(Z3Context::ZC, Model); }
+
+ /// Override implicit copy constructor for correct reference counting.
+ Z3Model(const Z3Model &Copy) : Model(Copy.Model) {
+ Z3_model_inc_ref(Z3Context::ZC, Model);
+ }
+
+ /// Provide move constructor
+ Z3Model(Z3Model &&Move) : Model(nullptr) { *this = std::move(Move); }
+
+ /// Provide move assignment constructor
+ Z3Model &operator=(Z3Model &&Move) {
+ if (this != &Move) {
+ if (Model)
+ Z3_model_dec_ref(Z3Context::ZC, Model);
+ Model = Move.Model;
+ Move.Model = nullptr;
+ }
+ return *this;
+ }
+
+ ~Z3Model() {
+ if (Model)
+ Z3_model_dec_ref(Z3Context::ZC, Model);
+ }
+
+ /// Given an expression, extract the value of this operand in the model.
+ bool getInterpretation(const Z3Expr &Exp, llvm::APSInt &Int) const {
+ Z3_func_decl Func =
+ Z3_get_app_decl(Z3Context::ZC, Z3_to_app(Z3Context::ZC, Exp.AST));
+ if (Z3_model_has_interp(Z3Context::ZC, Model, Func) != Z3_L_TRUE)
+ return false;
+
+ Z3_ast Assign = Z3_model_get_const_interp(Z3Context::ZC, Model, Func);
+ Z3Sort Sort = Z3Sort::getSort(Assign);
+ return Z3Expr::toAPSInt(Sort, Assign, Int, true);
+ }
+
+ /// Given an expression, extract the value of this operand in the model.
+ bool getInterpretation(const Z3Expr &Exp, llvm::APFloat &Float) const {
+ Z3_func_decl Func =
+ Z3_get_app_decl(Z3Context::ZC, Z3_to_app(Z3Context::ZC, Exp.AST));
+ if (Z3_model_has_interp(Z3Context::ZC, Model, Func) != Z3_L_TRUE)
+ return false;
+
+ Z3_ast Assign = Z3_model_get_const_interp(Z3Context::ZC, Model, Func);
+ Z3Sort Sort = Z3Sort::getSort(Assign);
+ return Z3Expr::toAPFloat(Sort, Assign, Float, true);
+ }
+
+ void print(raw_ostream &OS) const {
+ OS << Z3_model_to_string(Z3Context::ZC, Model);
+ }
+
+ LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); }
+}; // end class Z3Model
+
+class Z3Solver {
+ friend class Z3ConstraintManager;
+
+ Z3_solver Solver;
+
+ Z3Solver(Z3_solver ZS) : Solver(ZS) {
+ Z3_solver_inc_ref(Z3Context::ZC, Solver);
+ }
+
+public:
+ /// Override implicit copy constructor for correct reference counting.
+ Z3Solver(const Z3Solver &Copy) : Solver(Copy.Solver) {
+ Z3_solver_inc_ref(Z3Context::ZC, Solver);
+ }
+
+ /// Provide move constructor
+ Z3Solver(Z3Solver &&Move) : Solver(nullptr) { *this = std::move(Move); }
+
+ /// Provide move assignment constructor
+ Z3Solver &operator=(Z3Solver &&Move) {
+ if (this != &Move) {
+ if (Solver)
+ Z3_solver_dec_ref(Z3Context::ZC, Solver);
+ Solver = Move.Solver;
+ Move.Solver = nullptr;
+ }
+ return *this;
+ }
+
+ ~Z3Solver() {
+ if (Solver)
+ Z3_solver_dec_ref(Z3Context::ZC, Solver);
+ }
+
+ /// Given a constraint, add it to the solver
+ void addConstraint(const Z3Expr &Exp) {
+ Z3_solver_assert(Z3Context::ZC, Solver, Exp.AST);
+ }
+
+ /// Given a program state, construct the logical conjunction and add it to
+ /// the solver
+ void addStateConstraints(ProgramStateRef State) {
+ // TODO: Don't add all the constraints, only the relevant ones
+ ConstraintZ3Ty CZ = State->get<ConstraintZ3>();
+ ConstraintZ3Ty::iterator I = CZ.begin(), IE = CZ.end();
+
+ // Construct the logical AND of all the constraints
+ if (I != IE) {
+ std::vector<Z3_ast> ASTs;
+
+ while (I != IE)
+ ASTs.push_back(I++->second.AST);
+
+ Z3Expr Conj = Z3Expr::fromNBinOp(BO_LAnd, ASTs);
+ addConstraint(Conj);
+ }
+ }
+
+ /// Check if the constraints are satisfiable
+ Z3_lbool check() { return Z3_solver_check(Z3Context::ZC, Solver); }
+
+ /// Push the current solver state
+ void push() { return Z3_solver_push(Z3Context::ZC, Solver); }
+
+ /// Pop the previous solver state
+ void pop(unsigned NumStates = 1) {
+ assert(Z3_solver_get_num_scopes(Z3Context::ZC, Solver) >= NumStates);
+ return Z3_solver_pop(Z3Context::ZC, Solver, NumStates);
+ }
+
+ /// Get a model from the solver. Caller should check the model is
+ /// satisfiable.
+ Z3Model getModel() {
+ return Z3Model(Z3_solver_get_model(Z3Context::ZC, Solver));
+ }
+
+ /// Reset the solver and remove all constraints.
+ void reset() { Z3_solver_reset(Z3Context::ZC, Solver); }
+}; // end class Z3Solver
+
+void Z3ErrorHandler(Z3_context Context, Z3_error_code Error) {
+ llvm::report_fatal_error("Z3 error: " +
+ llvm::Twine(Z3_get_error_msg_ex(Context, Error)));
+}
+
+class Z3ConstraintManager : public SimpleConstraintManager {
+ Z3Context Context;
+ mutable Z3Solver Solver;
+
+public:
+ Z3ConstraintManager(SubEngine *SE, SValBuilder &SB)
+ : SimpleConstraintManager(SE, SB),
+ Solver(Z3_mk_simple_solver(Z3Context::ZC)) {
+ Z3_set_error_handler(Z3Context::ZC, Z3ErrorHandler);
+ }
+
+ //===------------------------------------------------------------------===//
+ // Implementation for interface from ConstraintManager.
+ //===------------------------------------------------------------------===//
+
+ bool canReasonAbout(SVal X) const override;
+
+ ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override;
+
+ const llvm::APSInt *getSymVal(ProgramStateRef State,
+ SymbolRef Sym) const override;
+
+ ProgramStateRef removeDeadBindings(ProgramStateRef St,
+ SymbolReaper &SymReaper) override;
+
+ void print(ProgramStateRef St, raw_ostream &Out, const char *nl,
+ const char *sep) override;
+
+ //===------------------------------------------------------------------===//
+ // Implementation for interface from SimpleConstraintManager.
+ //===------------------------------------------------------------------===//
+
+ ProgramStateRef assumeSym(ProgramStateRef state, SymbolRef Sym,
+ bool Assumption) override;
+
+ ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &From,
+ const llvm::APSInt &To,
+ bool InRange) override;
+
+ ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym,
+ bool Assumption) override;
+
+private:
+ //===------------------------------------------------------------------===//
+ // Internal implementation.
+ //===------------------------------------------------------------------===//
+
+ // Check whether a new model is satisfiable, and update the program state.
+ ProgramStateRef assumeZ3Expr(ProgramStateRef State, SymbolRef Sym,
+ const Z3Expr &Exp);
+
+ // Generate and check a Z3 model, using the given constraint.
+ Z3_lbool checkZ3Model(ProgramStateRef State, const Z3Expr &Exp) const;
+
+ // Generate a Z3Expr that represents the given symbolic expression.
+ // Sets the hasComparison parameter if the expression has a comparison
+ // operator.
+ // Sets the RetTy parameter to the final return type after promotions and
+ // casts.
+ Z3Expr getZ3Expr(SymbolRef Sym, QualType *RetTy = nullptr,
+ bool *hasComparison = nullptr) const;
+
+ // Generate a Z3Expr that takes the logical not of an expression.
+ Z3Expr getZ3NotExpr(const Z3Expr &Exp) const;
+
+ // Generate a Z3Expr that compares the expression to zero.
+ Z3Expr getZ3ZeroExpr(const Z3Expr &Exp, QualType RetTy,
+ bool Assumption) const;
+
+ // Recursive implementation to unpack and generate symbolic expression.
+ // Sets the hasComparison and RetTy parameters. See getZ3Expr().
+ Z3Expr getZ3SymExpr(SymbolRef Sym, QualType *RetTy,
+ bool *hasComparison) const;
+
+ // Wrapper to generate Z3Expr from SymbolData.
+ Z3Expr getZ3DataExpr(const SymbolID ID, QualType Ty) const;
+
+ // Wrapper to generate Z3Expr from SymbolCast.
+ Z3Expr getZ3CastExpr(const Z3Expr &Exp, QualType FromTy, QualType Ty) const;
+
+ // Wrapper to generate Z3Expr from BinarySymExpr.
+ // Sets the hasComparison and RetTy parameters. See getZ3Expr().
+ Z3Expr getZ3SymBinExpr(const BinarySymExpr *BSE, bool *hasComparison,
+ QualType *RetTy) const;
+
+ // Wrapper to generate Z3Expr from unpacked binary symbolic expression.
+ // Sets the RetTy parameter. See getZ3Expr().
+ Z3Expr getZ3BinExpr(const Z3Expr &LHS, QualType LTy,
+ BinaryOperator::Opcode Op, const Z3Expr &RHS,
+ QualType RTy, QualType *RetTy) const;
+
+ //===------------------------------------------------------------------===//
+ // Helper functions.
+ //===------------------------------------------------------------------===//
+
+ // Recover the QualType of an APSInt.
+ // TODO: Refactor to put elsewhere
+ QualType getAPSIntType(const llvm::APSInt &Int) const;
+
+ // Perform implicit type conversion on binary symbolic expressions.
+ // May modify all input parameters.
+ // TODO: Refactor to use built-in conversion functions
+ void doTypeConversion(Z3Expr &LHS, Z3Expr &RHS, QualType &LTy,
+ QualType &RTy) const;
+
+ // Perform implicit integer type conversion.
+ // May modify all input parameters.
+ // TODO: Refactor to use Sema::handleIntegerConversion()
+ template <typename T,
+ T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)>
+ void doIntTypeConversion(T &LHS, QualType &LTy, T &RHS, QualType &RTy) const;
+
+ // Perform implicit floating-point type conversion.
+ // May modify all input parameters.
+ // TODO: Refactor to use Sema::handleFloatConversion()
+ template <typename T,
+ T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)>
+ void doFloatTypeConversion(T &LHS, QualType &LTy, T &RHS,
+ QualType &RTy) const;
+
+ // Callback function for doCast parameter on APSInt type.
+ static llvm::APSInt castAPSInt(const llvm::APSInt &V, QualType ToTy,
+ uint64_t ToWidth, QualType FromTy,
+ uint64_t FromWidth);
+}; // end class Z3ConstraintManager
+
+Z3_context Z3Context::ZC;
+
+} // end anonymous namespace
+
+ProgramStateRef Z3ConstraintManager::assumeSym(ProgramStateRef State,
+ SymbolRef Sym, bool Assumption) {
+ QualType RetTy;
+ bool hasComparison;
+
+ Z3Expr Exp = getZ3Expr(Sym, &RetTy, &hasComparison);
+ // Create zero comparison for implicit boolean cast, with reversed assumption
+ if (!hasComparison && !RetTy->isBooleanType())
+ return assumeZ3Expr(State, Sym, getZ3ZeroExpr(Exp, RetTy, !Assumption));
+
+ return assumeZ3Expr(State, Sym, Assumption ? Exp : getZ3NotExpr(Exp));
+}
+
+ProgramStateRef Z3ConstraintManager::assumeSymInclusiveRange(
+ ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
+ const llvm::APSInt &To, bool InRange) {
+ QualType RetTy;
+ // The expression may be casted, so we cannot call getZ3DataExpr() directly
+ Z3Expr Exp = getZ3Expr(Sym, &RetTy);
+
+ assert((getAPSIntType(From) == getAPSIntType(To)) &&
+ "Range values have different types!");
+ QualType RTy = getAPSIntType(From);
+ bool isSignedTy = RetTy->isSignedIntegerOrEnumerationType();
+ Z3Expr FromExp = Z3Expr::fromAPSInt(From);
+ Z3Expr ToExp = Z3Expr::fromAPSInt(To);
+
+ // Construct single (in)equality
+ if (From == To)
+ return assumeZ3Expr(State, Sym,
+ getZ3BinExpr(Exp, RetTy, InRange ? BO_EQ : BO_NE,
+ FromExp, RTy, nullptr));
+
+ // Construct two (in)equalities, and a logical and/or
+ Z3Expr LHS =
+ getZ3BinExpr(Exp, RetTy, InRange ? BO_GE : BO_LT, FromExp, RTy, nullptr);
+ Z3Expr RHS =
+ getZ3BinExpr(Exp, RetTy, InRange ? BO_LE : BO_GT, ToExp, RTy, nullptr);
+ return assumeZ3Expr(
+ State, Sym,
+ Z3Expr::fromBinOp(LHS, InRange ? BO_LAnd : BO_LOr, RHS, isSignedTy));
+}
+
+ProgramStateRef Z3ConstraintManager::assumeSymUnsupported(ProgramStateRef State,
+ SymbolRef Sym,
+ bool Assumption) {
+ // Skip anything that is unsupported
+ return State;
+}
+
+bool Z3ConstraintManager::canReasonAbout(SVal X) const {
+ const TargetInfo &TI = getBasicVals().getContext().getTargetInfo();
+
+ Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
+ if (!SymVal)
+ return true;
+
+ const SymExpr *Sym = SymVal->getSymbol();
+ do {
+ QualType Ty = Sym->getType();
+
+ // Complex types are not modeled
+ if (Ty->isComplexType() || Ty->isComplexIntegerType())
+ return false;
+
+ // Non-IEEE 754 floating-point types are not modeled
+ if ((Ty->isSpecificBuiltinType(BuiltinType::LongDouble) &&
+ (&TI.getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended() ||
+ &TI.getLongDoubleFormat() == &llvm::APFloat::PPCDoubleDouble())))
+ return false;
+
+ if (isa<SymbolData>(Sym)) {
+ break;
+ } else if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) {
+ Sym = SC->getOperand();
+ } else if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
+ if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) {
+ Sym = SIE->getLHS();
+ } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) {
+ Sym = ISE->getRHS();
+ } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) {
+ return canReasonAbout(nonloc::SymbolVal(SSM->getLHS())) &&
+ canReasonAbout(nonloc::SymbolVal(SSM->getRHS()));
+ } else {
+ llvm_unreachable("Unsupported binary expression to reason about!");
+ }
+ } else {
+ llvm_unreachable("Unsupported expression to reason about!");
+ }
+ } while (Sym);
+
+ return true;
+}
+
+ConditionTruthVal Z3ConstraintManager::checkNull(ProgramStateRef State,
+ SymbolRef Sym) {
+ QualType RetTy;
+ // The expression may be casted, so we cannot call getZ3DataExpr() directly
+ Z3Expr VarExp = getZ3Expr(Sym, &RetTy);
+ Z3Expr Exp = getZ3ZeroExpr(VarExp, RetTy, true);
+ // Negate the constraint
+ Z3Expr NotExp = getZ3ZeroExpr(VarExp, RetTy, false);
+
+ Solver.reset();
+ Solver.addStateConstraints(State);
+
+ Solver.push();
+ Solver.addConstraint(Exp);
+ Z3_lbool isSat = Solver.check();
+
+ Solver.pop();
+ Solver.addConstraint(NotExp);
+ Z3_lbool isNotSat = Solver.check();
+
+ // Zero is the only possible solution
+ if (isSat == Z3_L_TRUE && isNotSat == Z3_L_FALSE)
+ return true;
+ // Zero is not a solution
+ else if (isSat == Z3_L_FALSE && isNotSat == Z3_L_TRUE)
+ return false;
+
+ // Zero may be a solution
+ return ConditionTruthVal();
+}
+
+const llvm::APSInt *Z3ConstraintManager::getSymVal(ProgramStateRef State,
+ SymbolRef Sym) const {
+ BasicValueFactory &BV = getBasicVals();
+ ASTContext &Ctx = BV.getContext();
+
+ if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) {
+ QualType Ty = Sym->getType();
+ assert(!Ty->isRealFloatingType());
+ llvm::APSInt Value(Ctx.getTypeSize(Ty),
+ !Ty->isSignedIntegerOrEnumerationType());
+
+ 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::fromBinOp(
+ Exp, BO_NE,
+ Ty->isBooleanType() ? Z3Expr::fromBoolean(Value.getBoolValue())
+ : Z3Expr::fromAPSInt(Value),
+ false);
+
+ Solver.addConstraint(NotExp);
+ if (Solver.check() == Z3_L_TRUE)
+ return nullptr;
+
+ // This is the only solution, store it
+ return &BV.getValue(Value);
+ } else if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) {
+ SymbolRef CastSym = SC->getOperand();
+ QualType CastTy = SC->getType();
+ // Skip the void type
+ if (CastTy->isVoidType())
+ return nullptr;
+
+ const llvm::APSInt *Value;
+ if (!(Value = getSymVal(State, CastSym)))
+ return nullptr;
+ return &BV.Convert(SC->getType(), *Value);
+ } else if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
+ const llvm::APSInt *LHS, *RHS;
+ if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) {
+ LHS = getSymVal(State, SIE->getLHS());
+ RHS = &SIE->getRHS();
+ } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) {
+ LHS = &ISE->getLHS();
+ RHS = getSymVal(State, ISE->getRHS());
+ } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) {
+ // Early termination to avoid expensive call
+ LHS = getSymVal(State, SSM->getLHS());
+ RHS = LHS ? getSymVal(State, SSM->getRHS()) : nullptr;
+ } else {
+ llvm_unreachable("Unsupported binary expression to get symbol value!");
+ }
+
+ if (!LHS || !RHS)
+ return nullptr;
+
+ llvm::APSInt ConvertedLHS = *LHS, ConvertedRHS = *RHS;
+ QualType LTy = getAPSIntType(*LHS), RTy = getAPSIntType(*RHS);
+ doIntTypeConversion<llvm::APSInt, Z3ConstraintManager::castAPSInt>(
+ ConvertedLHS, LTy, ConvertedRHS, RTy);
+ return BV.evalAPSInt(BSE->getOpcode(), ConvertedLHS, ConvertedRHS);
+ }
+
+ llvm_unreachable("Unsupported expression to get symbol value!");
+}
+
+ProgramStateRef
+Z3ConstraintManager::removeDeadBindings(ProgramStateRef State,
+ SymbolReaper &SymReaper) {
+ ConstraintZ3Ty CZ = State->get<ConstraintZ3>();
+ ConstraintZ3Ty::Factory &CZFactory = State->get_context<ConstraintZ3>();
+
+ for (ConstraintZ3Ty::iterator I = CZ.begin(), E = CZ.end(); I != E; ++I) {
+ if (SymReaper.maybeDead(I->first))
+ CZ = CZFactory.remove(CZ, *I);
+ }
+
+ return State->set<ConstraintZ3>(CZ);
+}
+
+//===------------------------------------------------------------------===//
+// Internal implementation.
+//===------------------------------------------------------------------===//
+
+ProgramStateRef Z3ConstraintManager::assumeZ3Expr(ProgramStateRef State,
+ SymbolRef Sym,
+ const Z3Expr &Exp) {
+ // Check the model, avoid simplifying AST to save time
+ if (checkZ3Model(State, Exp) == Z3_L_TRUE)
+ return State->add<ConstraintZ3>(std::make_pair(Sym, Exp));
+
+ return nullptr;
+}
+
+Z3_lbool Z3ConstraintManager::checkZ3Model(ProgramStateRef State,
+ const Z3Expr &Exp) const {
+ Solver.reset();
+ Solver.addConstraint(Exp);
+ Solver.addStateConstraints(State);
+ return Solver.check();
+}
+
+Z3Expr Z3ConstraintManager::getZ3Expr(SymbolRef Sym, QualType *RetTy,
+ bool *hasComparison) const {
+ if (hasComparison) {
+ *hasComparison = false;
+ }
+
+ return getZ3SymExpr(Sym, RetTy, hasComparison);
+}
+
+Z3Expr Z3ConstraintManager::getZ3NotExpr(const Z3Expr &Exp) const {
+ return Z3Expr::fromUnOp(UO_LNot, Exp);
+}
+
+Z3Expr Z3ConstraintManager::getZ3ZeroExpr(const Z3Expr &Exp, QualType Ty,
+ bool Assumption) const {
+ ASTContext &Ctx = getBasicVals().getContext();
+ if (Ty->isRealFloatingType()) {
+ llvm::APFloat Zero = llvm::APFloat::getZero(Ctx.getFloatTypeSemantics(Ty));
+ return Z3Expr::fromFloatBinOp(Exp, Assumption ? BO_EQ : BO_NE,
+ Z3Expr::fromAPFloat(Zero));
+ } else if (Ty->isIntegralOrEnumerationType() || Ty->isAnyPointerType() ||
+ Ty->isBlockPointerType() || Ty->isReferenceType()) {
+ bool isSigned = Ty->isSignedIntegerOrEnumerationType();
+ // Skip explicit comparison for boolean types
+ if (Ty->isBooleanType())
+ return Assumption ? getZ3NotExpr(Exp) : Exp;
+ return Z3Expr::fromBinOp(Exp, Assumption ? BO_EQ : BO_NE,
+ Z3Expr::fromInt("0", Ctx.getTypeSize(Ty)),
+ isSigned);
+ }
+
+ llvm_unreachable("Unsupported type for zero value!");
+}
+
+Z3Expr Z3ConstraintManager::getZ3SymExpr(SymbolRef Sym, QualType *RetTy,
+ bool *hasComparison) const {
+ if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) {
+ if (RetTy)
+ *RetTy = Sym->getType();
+
+ return getZ3DataExpr(SD->getSymbolID(), Sym->getType());
+ } else if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) {
+ if (RetTy)
+ *RetTy = Sym->getType();
+
+ QualType FromTy;
+ Z3Expr Exp = getZ3SymExpr(SC->getOperand(), &FromTy, hasComparison);
+ // Casting an expression with a comparison invalidates it. Note that this
+ // must occur after the recursive call above.
+ // e.g. (signed char) (x > 0)
+ if (hasComparison)
+ *hasComparison = false;
+ return getZ3CastExpr(Exp, FromTy, Sym->getType());
+ } else if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
+ Z3Expr Exp = getZ3SymBinExpr(BSE, hasComparison, RetTy);
+ // Set the hasComparison parameter, in post-order traversal order.
+ if (hasComparison)
+ *hasComparison = BinaryOperator::isComparisonOp(BSE->getOpcode());
+ return Exp;
+ }
+
+ llvm_unreachable("Unsupported SymbolRef type!");
+}
+
+Z3Expr Z3ConstraintManager::getZ3DataExpr(const SymbolID ID,
+ QualType Ty) const {
+ ASTContext &Ctx = getBasicVals().getContext();
+ return Z3Expr::fromData(ID, Ty->isBooleanType(), Ty->isRealFloatingType(),
+ Ctx.getTypeSize(Ty));
+}
+
+Z3Expr Z3ConstraintManager::getZ3CastExpr(const Z3Expr &Exp, QualType FromTy,
+ QualType ToTy) const {
+ ASTContext &Ctx = getBasicVals().getContext();
+ return Z3Expr::fromCast(Exp, ToTy, Ctx.getTypeSize(ToTy), FromTy,
+ Ctx.getTypeSize(FromTy));
+}
+
+Z3Expr Z3ConstraintManager::getZ3SymBinExpr(const BinarySymExpr *BSE,
+ bool *hasComparison,
+ QualType *RetTy) const {
+ QualType LTy, RTy;
+ BinaryOperator::Opcode Op = BSE->getOpcode();
+
+ if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) {
+ RTy = getAPSIntType(SIE->getRHS());
+ Z3Expr LHS = getZ3SymExpr(SIE->getLHS(), &LTy, hasComparison);
+ Z3Expr RHS = Z3Expr::fromAPSInt(SIE->getRHS());
+ return getZ3BinExpr(LHS, LTy, Op, RHS, RTy, RetTy);
+ } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) {
+ LTy = getAPSIntType(ISE->getLHS());
+ Z3Expr LHS = Z3Expr::fromAPSInt(ISE->getLHS());
+ Z3Expr RHS = getZ3SymExpr(ISE->getRHS(), &RTy, hasComparison);
+ return getZ3BinExpr(LHS, LTy, Op, RHS, RTy, RetTy);
+ } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) {
+ Z3Expr LHS = getZ3SymExpr(SSM->getLHS(), &LTy, hasComparison);
+ Z3Expr RHS = getZ3SymExpr(SSM->getRHS(), &RTy, hasComparison);
+ return getZ3BinExpr(LHS, LTy, Op, RHS, RTy, RetTy);
+ } else {
+ llvm_unreachable("Unsupported BinarySymExpr type!");
+ }
+}
+
+Z3Expr Z3ConstraintManager::getZ3BinExpr(const Z3Expr &LHS, QualType LTy,
+ BinaryOperator::Opcode Op,
+ const Z3Expr &RHS, QualType RTy,
+ QualType *RetTy) const {
+ Z3Expr NewLHS = LHS;
+ Z3Expr NewRHS = RHS;
+ doTypeConversion(NewLHS, NewRHS, LTy, RTy);
+ // Update the return type parameter if the output type has changed.
+ if (RetTy) {
+ // A boolean result can be represented as an integer type in C/C++, but at
+ // this point we only care about the Z3 type. Set it as a boolean type to
+ // avoid subsequent Z3 errors.
+ if (BinaryOperator::isComparisonOp(Op) || BinaryOperator::isLogicalOp(Op)) {
+ ASTContext &Ctx = getBasicVals().getContext();
+ *RetTy = Ctx.BoolTy;
+ } else {
+ *RetTy = LTy;
+ }
+
+ // If the two operands are pointers and the operation is a subtraction, the
+ // result is of type ptrdiff_t, which is signed
+ if (LTy->isAnyPointerType() && LTy == RTy && Op == BO_Sub) {
+ ASTContext &Ctx = getBasicVals().getContext();
+ *RetTy = Ctx.getIntTypeForBitwidth(Ctx.getTypeSize(LTy), true);
+ }
+ }
+
+ return LTy->isRealFloatingType()
+ ? Z3Expr::fromFloatBinOp(NewLHS, Op, NewRHS)
+ : Z3Expr::fromBinOp(NewLHS, Op, NewRHS,
+ LTy->isSignedIntegerOrEnumerationType());
+}
+
+//===------------------------------------------------------------------===//
+// Helper functions.
+//===------------------------------------------------------------------===//
+
+QualType Z3ConstraintManager::getAPSIntType(const llvm::APSInt &Int) const {
+ ASTContext &Ctx = getBasicVals().getContext();
+ return Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned());
+}
+
+void Z3ConstraintManager::doTypeConversion(Z3Expr &LHS, Z3Expr &RHS,
+ QualType &LTy, QualType &RTy) const {
+ ASTContext &Ctx = getBasicVals().getContext();
+
+ // Perform type conversion
+ if (LTy->isIntegralOrEnumerationType() &&
+ RTy->isIntegralOrEnumerationType()) {
+ if (LTy->isArithmeticType() && RTy->isArithmeticType())
+ return doIntTypeConversion<Z3Expr, Z3Expr::fromCast>(LHS, LTy, RHS, RTy);
+ } else if (LTy->isRealFloatingType() || RTy->isRealFloatingType()) {
+ return doFloatTypeConversion<Z3Expr, Z3Expr::fromCast>(LHS, LTy, RHS, RTy);
+ } else if ((LTy->isAnyPointerType() || RTy->isAnyPointerType()) ||
+ (LTy->isBlockPointerType() || RTy->isBlockPointerType()) ||
+ (LTy->isReferenceType() || RTy->isReferenceType())) {
+ // TODO: Refactor to Sema::FindCompositePointerType(), and
+ // Sema::CheckCompareOperands().
+
+ uint64_t LBitWidth = Ctx.getTypeSize(LTy);
+ uint64_t RBitWidth = Ctx.getTypeSize(RTy);
+
+ // Cast the non-pointer type to the pointer type.
+ // TODO: Be more strict about this.
+ if ((LTy->isAnyPointerType() ^ RTy->isAnyPointerType()) ||
+ (LTy->isBlockPointerType() ^ RTy->isBlockPointerType()) ||
+ (LTy->isReferenceType() ^ RTy->isReferenceType())) {
+ if (LTy->isNullPtrType() || LTy->isBlockPointerType() ||
+ LTy->isReferenceType()) {
+ LHS = Z3Expr::fromCast(LHS, RTy, RBitWidth, LTy, LBitWidth);
+ LTy = RTy;
+ } else {
+ RHS = Z3Expr::fromCast(RHS, LTy, LBitWidth, RTy, RBitWidth);
+ RTy = LTy;
+ }
+ }
+
+ // Cast the void pointer type to the non-void pointer type.
+ // For void types, this assumes that the casted value is equal to the value
+ // of the original pointer, and does not account for alignment requirements.
+ if (LTy->isVoidPointerType() ^ RTy->isVoidPointerType()) {
+ assert((Ctx.getTypeSize(LTy) == Ctx.getTypeSize(RTy)) &&
+ "Pointer types have different bitwidths!");
+ if (RTy->isVoidPointerType())
+ RTy = LTy;
+ else
+ LTy = RTy;
+ }
+
+ if (LTy == RTy)
+ return;
+ }
+
+ // Fallback: for the solver, assume that these types don't really matter
+ if ((LTy.getCanonicalType() == RTy.getCanonicalType()) ||
+ (LTy->isObjCObjectPointerType() && RTy->isObjCObjectPointerType())) {
+ LTy = RTy;
+ return;
+ }
+
+ // TODO: Refine behavior for invalid type casts
+}
+
+template <typename T,
+ T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)>
+void Z3ConstraintManager::doIntTypeConversion(T &LHS, QualType &LTy, T &RHS,
+ QualType &RTy) const {
+ ASTContext &Ctx = getBasicVals().getContext();
+
+ uint64_t LBitWidth = Ctx.getTypeSize(LTy);
+ uint64_t RBitWidth = Ctx.getTypeSize(RTy);
+
+ // Always perform integer promotion before checking type equality.
+ // Otherwise, e.g. (bool) a + (bool) b could trigger a backend assertion
+ if (LTy->isPromotableIntegerType()) {
+ QualType NewTy = Ctx.getPromotedIntegerType(LTy);
+ uint64_t NewBitWidth = Ctx.getTypeSize(NewTy);
+ LHS = (*doCast)(LHS, NewTy, NewBitWidth, LTy, LBitWidth);
+ LTy = NewTy;
+ LBitWidth = NewBitWidth;
+ }
+ if (RTy->isPromotableIntegerType()) {
+ QualType NewTy = Ctx.getPromotedIntegerType(RTy);
+ uint64_t NewBitWidth = Ctx.getTypeSize(NewTy);
+ RHS = (*doCast)(RHS, NewTy, NewBitWidth, RTy, RBitWidth);
+ RTy = NewTy;
+ RBitWidth = NewBitWidth;
+ }
+
+ if (LTy == RTy)
+ return;
+
+ // Perform integer type conversion
+ // Note: Safe to skip updating bitwidth because this must terminate
+ bool isLSignedTy = LTy->isSignedIntegerOrEnumerationType();
+ bool isRSignedTy = RTy->isSignedIntegerOrEnumerationType();
+
+ int order = Ctx.getIntegerTypeOrder(LTy, RTy);
+ if (isLSignedTy == isRSignedTy) {
+ // Same signedness; use the higher-ranked type
+ if (order == 1) {
+ RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth);
+ RTy = LTy;
+ } else {
+ LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth);
+ LTy = RTy;
+ }
+ } else if (order != (isLSignedTy ? 1 : -1)) {
+ // The unsigned type has greater than or equal rank to the
+ // signed type, so use the unsigned type
+ if (isRSignedTy) {
+ RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth);
+ RTy = LTy;
+ } else {
+ LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth);
+ LTy = RTy;
+ }
+ } else if (LBitWidth != RBitWidth) {
+ // The two types are different widths; if we are here, that
+ // means the signed type is larger than the unsigned type, so
+ // use the signed type.
+ if (isLSignedTy) {
+ RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth);
+ RTy = LTy;
+ } else {
+ LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth);
+ LTy = RTy;
+ }
+ } else {
+ // The signed type is higher-ranked than the unsigned type,
+ // but isn't actually any bigger (like unsigned int and long
+ // on most 32-bit systems). Use the unsigned type corresponding
+ // to the signed type.
+ QualType NewTy = Ctx.getCorrespondingUnsignedType(isLSignedTy ? LTy : RTy);
+ RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth);
+ RTy = NewTy;
+ LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth);
+ LTy = NewTy;
+ }
+}
+
+template <typename T,
+ T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)>
+void Z3ConstraintManager::doFloatTypeConversion(T &LHS, QualType &LTy, T &RHS,
+ QualType &RTy) const {
+ ASTContext &Ctx = getBasicVals().getContext();
+
+ uint64_t LBitWidth = Ctx.getTypeSize(LTy);
+ uint64_t RBitWidth = Ctx.getTypeSize(RTy);
+
+ // Perform float-point type promotion
+ if (!LTy->isRealFloatingType()) {
+ LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth);
+ LTy = RTy;
+ LBitWidth = RBitWidth;
+ }
+ if (!RTy->isRealFloatingType()) {
+ RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth);
+ RTy = LTy;
+ RBitWidth = LBitWidth;
+ }
+
+ if (LTy == RTy)
+ return;
+
+ // If we have two real floating types, convert the smaller operand to the
+ // bigger result
+ // 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);
+ RTy = LTy;
+ } else if (order == 0) {
+ LHS = Z3Expr::fromCast(LHS, RTy, RBitWidth, LTy, LBitWidth);
+ LTy = RTy;
+ } else {
+ llvm_unreachable("Unsupported floating-point type cast!");
+ }
+}
+
+llvm::APSInt Z3ConstraintManager::castAPSInt(const llvm::APSInt &V,
+ QualType ToTy, uint64_t ToWidth,
+ QualType FromTy,
+ uint64_t FromWidth) {
+ APSIntType TargetType(ToWidth, !ToTy->isSignedIntegerOrEnumerationType());
+ return TargetType.convert(V);
+}
+
+//==------------------------------------------------------------------------==/
+// Pretty-printing.
+//==------------------------------------------------------------------------==/
+
+void Z3ConstraintManager::print(ProgramStateRef St, raw_ostream &OS,
+ const char *nl, const char *sep) {
+
+ ConstraintZ3Ty CZ = St->get<ConstraintZ3>();
+
+ OS << nl << sep << "Constraints:";
+ for (ConstraintZ3Ty::iterator I = CZ.begin(), E = CZ.end(); I != E; ++I) {
+ OS << nl << ' ' << I->first << " : ";
+ I->second.print(OS);
+ }
+ OS << nl;
+}
+
+#endif
+
+std::unique_ptr<ConstraintManager>
+ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) {
+#if CLANG_ANALYZER_WITH_Z3
+ return llvm::make_unique<Z3ConstraintManager>(Eng, StMgr.getSValBuilder());
+#else
+ llvm::report_fatal_error("Clang was not compiled with Z3 support!", false);
+ return nullptr;
+#endif
+}