blob: 1a645cb8701154d6c2c29958ea3ac3f775c4a6e0 [file] [log] [blame]
//== SMTConstraintManager.h -------------------------------------*- 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 a SMT generic API, which will be the base class for
// every SMT solver specific class.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONSTRAINTMANAGER_H
#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONSTRAINTMANAGER_H
#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h"
namespace clang {
namespace ento {
template <typename ConstraintSMT, typename SMTExprTy>
class SMTConstraintManager : public clang::ento::SimpleConstraintManager {
SMTSolverRef &Solver;
public:
SMTConstraintManager(clang::ento::SubEngine *SE, clang::ento::SValBuilder &SB,
SMTSolverRef &S)
: SimpleConstraintManager(SE, SB), Solver(S) {}
virtual ~SMTConstraintManager() = default;
//===------------------------------------------------------------------===//
// Implementation for interface from SimpleConstraintManager.
//===------------------------------------------------------------------===//
ProgramStateRef assumeSym(ProgramStateRef State, SymbolRef Sym,
bool Assumption) override {
ASTContext &Ctx = getBasicVals().getContext();
QualType RetTy;
bool hasComparison;
SMTExprRef Exp = SMTConv::getExpr(Solver, Ctx, Sym, &RetTy, &hasComparison);
// Create zero comparison for implicit boolean cast, with reversed
// assumption
if (!hasComparison && !RetTy->isBooleanType())
return assumeExpr(
State, Sym,
SMTConv::getZeroExpr(Solver, Ctx, Exp, RetTy, !Assumption));
return assumeExpr(State, Sym, Assumption ? Exp : Solver->mkNot(Exp));
}
ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym,
const llvm::APSInt &From,
const llvm::APSInt &To,
bool InRange) override {
ASTContext &Ctx = getBasicVals().getContext();
return assumeExpr(
State, Sym, SMTConv::getRangeExpr(Solver, Ctx, Sym, From, To, InRange));
}
ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym,
bool Assumption) override {
// Skip anything that is unsupported
return State;
}
//===------------------------------------------------------------------===//
// Implementation for interface from ConstraintManager.
//===------------------------------------------------------------------===//
ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override {
ASTContext &Ctx = getBasicVals().getContext();
QualType RetTy;
// The expression may be casted, so we cannot call getZ3DataExpr() directly
SMTExprRef VarExp = SMTConv::getExpr(Solver, Ctx, Sym, &RetTy);
SMTExprRef Exp =
SMTConv::getZeroExpr(Solver, Ctx, VarExp, RetTy, /*Assumption=*/true);
// Negate the constraint
SMTExprRef NotExp =
SMTConv::getZeroExpr(Solver, Ctx, VarExp, RetTy, /*Assumption=*/false);
ConditionTruthVal isSat = checkModel(State, Sym, Exp);
ConditionTruthVal isNotSat = checkModel(State, Sym, NotExp);
// Zero is the only possible solution
if (isSat.isConstrainedTrue() && isNotSat.isConstrainedFalse())
return true;
// Zero is not a solution
if (isSat.isConstrainedFalse() && isNotSat.isConstrainedTrue())
return false;
// Zero may be a solution
return ConditionTruthVal();
}
const llvm::APSInt *getSymVal(ProgramStateRef State,
SymbolRef Sym) const override {
BasicValueFactory &BVF = getBasicVals();
ASTContext &Ctx = BVF.getContext();
if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) {
QualType Ty = Sym->getType();
assert(!Ty->isRealFloatingType());
llvm::APSInt Value(Ctx.getTypeSize(Ty),
!Ty->isSignedIntegerOrEnumerationType());
// TODO: this should call checkModel so we can use the cache, however,
// this method tries to get the interpretation (the actual value) from
// the solver, which is currently not cached.
SMTExprRef Exp =
SMTConv::fromData(Solver, SD->getSymbolID(), Ty, Ctx.getTypeSize(Ty));
Solver->reset();
addStateConstraints(State);
// Constraints are unsatisfiable
Optional<bool> isSat = Solver->check();
if (!isSat.hasValue() || !isSat.getValue())
return nullptr;
// Model does not assign interpretation
if (!Solver->getInterpretation(Exp, Value))
return nullptr;
// A value has been obtained, check if it is the only value
SMTExprRef NotExp = SMTConv::fromBinOp(
Solver, Exp, BO_NE,
Ty->isBooleanType() ? Solver->fromBoolean(Value.getBoolValue())
: Solver->fromAPSInt(Value),
false);
Solver->addConstraint(NotExp);
Optional<bool> isNotSat = Solver->check();
if (!isSat.hasValue() || isNotSat.getValue())
return nullptr;
// This is the only solution, store it
return &BVF.getValue(Value);
}
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 &BVF.Convert(SC->getType(), *Value);
}
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, ConvertedRHS;
QualType LTy, RTy;
std::tie(ConvertedLHS, LTy) = SMTConv::fixAPSInt(Ctx, *LHS);
std::tie(ConvertedRHS, RTy) = SMTConv::fixAPSInt(Ctx, *RHS);
SMTConv::doIntTypeConversion<llvm::APSInt, &SMTConv::castAPSInt>(
Solver, Ctx, ConvertedLHS, LTy, ConvertedRHS, RTy);
return BVF.evalAPSInt(BSE->getOpcode(), ConvertedLHS, ConvertedRHS);
}
llvm_unreachable("Unsupported expression to get symbol value!");
}
ProgramStateRef removeDeadBindings(ProgramStateRef State,
SymbolReaper &SymReaper) override {
auto CZ = State->get<ConstraintSMT>();
auto &CZFactory = State->get_context<ConstraintSMT>();
for (auto I = CZ.begin(), E = CZ.end(); I != E; ++I) {
if (SymReaper.maybeDead(I->first))
CZ = CZFactory.remove(CZ, *I);
}
return State->set<ConstraintSMT>(CZ);
}
void print(ProgramStateRef St, raw_ostream &OS, const char *nl,
const char *sep) override {
auto CZ = St->get<ConstraintSMT>();
OS << nl << sep << "Constraints:";
for (auto I = CZ.begin(), E = CZ.end(); I != E; ++I) {
OS << nl << ' ' << I->first << " : ";
I->second.print(OS);
}
OS << nl;
}
/// Dumps SMT formula
LLVM_DUMP_METHOD void dump() const { Solver->dump(); }
protected:
// Check whether a new model is satisfiable, and update the program state.
virtual ProgramStateRef assumeExpr(ProgramStateRef State, SymbolRef Sym,
const SMTExprRef &Exp) {
// Check the model, avoid simplifying AST to save time
if (checkModel(State, Sym, Exp).isConstrainedTrue())
return State->add<ConstraintSMT>(
std::make_pair(Sym, static_cast<const SMTExprTy &>(*Exp)));
return nullptr;
}
/// Given a program state, construct the logical conjunction and add it to
/// the solver
virtual void addStateConstraints(ProgramStateRef State) const {
// TODO: Don't add all the constraints, only the relevant ones
auto CZ = State->get<ConstraintSMT>();
auto I = CZ.begin(), IE = CZ.end();
// Construct the logical AND of all the constraints
if (I != IE) {
std::vector<SMTExprRef> ASTs;
SMTExprRef Constraint = Solver->newExprRef(I++->second);
while (I != IE) {
Constraint = Solver->mkAnd(Constraint, Solver->newExprRef(I++->second));
}
Solver->addConstraint(Constraint);
}
}
// Generate and check a Z3 model, using the given constraint.
ConditionTruthVal checkModel(ProgramStateRef State, SymbolRef Sym,
const SMTExprRef &Exp) const {
ProgramStateRef NewState = State->add<ConstraintSMT>(
std::make_pair(Sym, static_cast<const SMTExprTy &>(*Exp)));
llvm::FoldingSetNodeID ID;
NewState->get<ConstraintSMT>().Profile(ID);
unsigned hash = ID.ComputeHash();
auto I = Cached.find(hash);
if (I != Cached.end())
return I->second;
Solver->reset();
addStateConstraints(NewState);
Optional<bool> res = Solver->check();
if (!res.hasValue())
Cached[hash] = ConditionTruthVal();
else
Cached[hash] = ConditionTruthVal(res.getValue());
return Cached[hash];
}
// Cache the result of an SMT query (true, false, unknown). The key is the
// hash of the constraints in a state
mutable llvm::DenseMap<unsigned, ConditionTruthVal> Cached;
}; // end class SMTConstraintManager
} // namespace ento
} // namespace clang
#endif