clang 20.0.0git
SMTConstraintManager.h
Go to the documentation of this file.
1//== SMTConstraintManager.h -------------------------------------*- C++ -*--==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines a SMT generic API, which will be the base class for
10// every SMT solver specific class.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONSTRAINTMANAGER_H
15#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SMTCONSTRAINTMANAGER_H
16
22#include <optional>
23
24typedef llvm::ImmutableSet<
25 std::pair<clang::ento::SymbolRef, const llvm::SMTExpr *>>
28
29namespace clang {
30namespace ento {
31
33 mutable llvm::SMTSolverRef Solver = llvm::CreateZ3Solver();
34
35public:
38 : SimpleConstraintManager(EE, SB) {
39 Solver->setBoolParam("model", true); // Enable model finding
40 Solver->setUnsignedParam("timeout", 15000 /*milliseconds*/);
41 }
42 virtual ~SMTConstraintManager() = default;
43
44 //===------------------------------------------------------------------===//
45 // Implementation for interface from SimpleConstraintManager.
46 //===------------------------------------------------------------------===//
47
49 bool Assumption) override {
51
52 QualType RetTy;
53 bool hasComparison;
54
55 llvm::SMTExprRef Exp =
56 SMTConv::getExpr(Solver, Ctx, Sym, &RetTy, &hasComparison);
57
58 // Create zero comparison for implicit boolean cast, with reversed
59 // assumption
60 if (!hasComparison && !RetTy->isBooleanType())
61 return assumeExpr(
62 State, Sym,
63 SMTConv::getZeroExpr(Solver, Ctx, Exp, RetTy, !Assumption));
64
65 return assumeExpr(State, Sym, Assumption ? Exp : Solver->mkNot(Exp));
66 }
67
69 const llvm::APSInt &From,
70 const llvm::APSInt &To,
71 bool InRange) override {
73 return assumeExpr(
74 State, Sym, SMTConv::getRangeExpr(Solver, Ctx, Sym, From, To, InRange));
75 }
76
78 bool Assumption) override {
79 // Skip anything that is unsupported
80 return State;
81 }
82
83 //===------------------------------------------------------------------===//
84 // Implementation for interface from ConstraintManager.
85 //===------------------------------------------------------------------===//
86
89
90 QualType RetTy;
91 // The expression may be casted, so we cannot call getZ3DataExpr() directly
92 llvm::SMTExprRef VarExp = SMTConv::getExpr(Solver, Ctx, Sym, &RetTy);
93 llvm::SMTExprRef Exp =
94 SMTConv::getZeroExpr(Solver, Ctx, VarExp, RetTy, /*Assumption=*/true);
95
96 // Negate the constraint
97 llvm::SMTExprRef NotExp =
98 SMTConv::getZeroExpr(Solver, Ctx, VarExp, RetTy, /*Assumption=*/false);
99
100 ConditionTruthVal isSat = checkModel(State, Sym, Exp);
101 ConditionTruthVal isNotSat = checkModel(State, Sym, NotExp);
102
103 // Zero is the only possible solution
104 if (isSat.isConstrainedTrue() && isNotSat.isConstrainedFalse())
105 return true;
106
107 // Zero is not a solution
108 if (isSat.isConstrainedFalse() && isNotSat.isConstrainedTrue())
109 return false;
110
111 // Zero may be a solution
112 return ConditionTruthVal();
113 }
114
115 const llvm::APSInt *getSymVal(ProgramStateRef State,
116 SymbolRef Sym) const override {
118 ASTContext &Ctx = BVF.getContext();
119
120 if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) {
121 QualType Ty = Sym->getType();
122 assert(!Ty->isRealFloatingType());
123 llvm::APSInt Value(Ctx.getTypeSize(Ty),
125
126 // TODO: this should call checkModel so we can use the cache, however,
127 // this method tries to get the interpretation (the actual value) from
128 // the solver, which is currently not cached.
129
130 llvm::SMTExprRef Exp = SMTConv::fromData(Solver, Ctx, SD);
131
132 Solver->reset();
133 addStateConstraints(State);
134
135 // Constraints are unsatisfiable
136 std::optional<bool> isSat = Solver->check();
137 if (!isSat || !*isSat)
138 return nullptr;
139
140 // Model does not assign interpretation
141 if (!Solver->getInterpretation(Exp, Value))
142 return nullptr;
143
144 // A value has been obtained, check if it is the only value
145 llvm::SMTExprRef NotExp = SMTConv::fromBinOp(
146 Solver, Exp, BO_NE,
147 Ty->isBooleanType() ? Solver->mkBoolean(Value.getBoolValue())
148 : Solver->mkBitvector(Value, Value.getBitWidth()),
149 /*isSigned=*/false);
150
151 Solver->addConstraint(NotExp);
152
153 std::optional<bool> isNotSat = Solver->check();
154 if (!isNotSat || *isNotSat)
155 return nullptr;
156
157 // This is the only solution, store it
158 return BVF.getValue(Value).get();
159 }
160
161 if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) {
162 SymbolRef CastSym = SC->getOperand();
163 QualType CastTy = SC->getType();
164 // Skip the void type
165 if (CastTy->isVoidType())
166 return nullptr;
167
168 const llvm::APSInt *Value;
169 if (!(Value = getSymVal(State, CastSym)))
170 return nullptr;
171 return BVF.Convert(SC->getType(), *Value).get();
172 }
173
174 if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
175 const llvm::APSInt *LHS, *RHS;
176 if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) {
177 LHS = getSymVal(State, SIE->getLHS());
178 RHS = SIE->getRHS().get();
179 } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) {
180 LHS = ISE->getLHS().get();
181 RHS = getSymVal(State, ISE->getRHS());
182 } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) {
183 // Early termination to avoid expensive call
184 LHS = getSymVal(State, SSM->getLHS());
185 RHS = LHS ? getSymVal(State, SSM->getRHS()) : nullptr;
186 } else {
187 llvm_unreachable("Unsupported binary expression to get symbol value!");
188 }
189
190 if (!LHS || !RHS)
191 return nullptr;
192
193 llvm::APSInt ConvertedLHS, ConvertedRHS;
194 QualType LTy, RTy;
195 std::tie(ConvertedLHS, LTy) = SMTConv::fixAPSInt(Ctx, *LHS);
196 std::tie(ConvertedRHS, RTy) = SMTConv::fixAPSInt(Ctx, *RHS);
197 SMTConv::doIntTypeConversion<llvm::APSInt, &SMTConv::castAPSInt>(
198 Solver, Ctx, ConvertedLHS, LTy, ConvertedRHS, RTy);
199 std::optional<APSIntPtr> Res =
200 BVF.evalAPSInt(BSE->getOpcode(), ConvertedLHS, ConvertedRHS);
201 return Res ? Res.value().get() : nullptr;
202 }
203
204 llvm_unreachable("Unsupported expression to get symbol value!");
205 }
206
208 SymbolReaper &SymReaper) override {
209 auto CZ = State->get<ConstraintSMT>();
210 auto &CZFactory = State->get_context<ConstraintSMT>();
211
212 for (const auto &Entry : CZ) {
213 if (SymReaper.isDead(Entry.first))
214 CZ = CZFactory.remove(CZ, Entry);
215 }
216
217 return State->set<ConstraintSMT>(CZ);
218 }
219
220 void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n",
221 unsigned int Space = 0, bool IsDot = false) const override {
222 ConstraintSMTType Constraints = State->get<ConstraintSMT>();
223
224 Indent(Out, Space, IsDot) << "\"constraints\": ";
225 if (Constraints.isEmpty()) {
226 Out << "null," << NL;
227 return;
228 }
229
230 ++Space;
231 Out << '[' << NL;
232 for (ConstraintSMTType::iterator I = Constraints.begin();
233 I != Constraints.end(); ++I) {
234 Indent(Out, Space, IsDot)
235 << "{ \"symbol\": \"" << I->first << "\", \"range\": \"";
236 I->second->print(Out);
237 Out << "\" }";
238
239 if (std::next(I) != Constraints.end())
240 Out << ',';
241 Out << NL;
242 }
243
244 --Space;
245 Indent(Out, Space, IsDot) << "],";
246 }
247
249 ProgramStateRef S2) const override {
250 return S1->get<ConstraintSMT>() == S2->get<ConstraintSMT>();
251 }
252
253 bool canReasonAbout(SVal X) const override {
255
256 std::optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
257 if (!SymVal)
258 return true;
259
260 const SymExpr *Sym = SymVal->getSymbol();
261 QualType Ty = Sym->getType();
262
263 // Complex types are not modeled
264 if (Ty->isComplexType() || Ty->isComplexIntegerType())
265 return false;
266
267 // Non-IEEE 754 floating-point types are not modeled
268 if ((Ty->isSpecificBuiltinType(BuiltinType::LongDouble) &&
269 (&TI.getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended() ||
270 &TI.getLongDoubleFormat() == &llvm::APFloat::PPCDoubleDouble())))
271 return false;
272
273 if (Ty->isRealFloatingType())
274 return Solver->isFPSupported();
275
276 if (isa<SymbolData>(Sym))
277 return true;
278
280
281 if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym))
282 return canReasonAbout(SVB.makeSymbolVal(SC->getOperand()));
283
284 // UnarySymExpr support is not yet implemented in the Z3 wrapper.
285 if (isa<UnarySymExpr>(Sym)) {
286 return false;
287 }
288
289 if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
290 if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE))
291 return canReasonAbout(SVB.makeSymbolVal(SIE->getLHS()));
292
293 if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE))
294 return canReasonAbout(SVB.makeSymbolVal(ISE->getRHS()));
295
296 if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(BSE))
297 return canReasonAbout(SVB.makeSymbolVal(SSE->getLHS())) &&
298 canReasonAbout(SVB.makeSymbolVal(SSE->getRHS()));
299 }
300
301 llvm_unreachable("Unsupported expression to reason about!");
302 }
303
304 /// Dumps SMT formula
305 LLVM_DUMP_METHOD void dump() const { Solver->dump(); }
306
307protected:
308 // Check whether a new model is satisfiable, and update the program state.
310 const llvm::SMTExprRef &Exp) {
311 // Check the model, avoid simplifying AST to save time
312 if (checkModel(State, Sym, Exp).isConstrainedTrue())
313 return State->add<ConstraintSMT>(std::make_pair(Sym, Exp));
314
315 return nullptr;
316 }
317
318 /// Given a program state, construct the logical conjunction and add it to
319 /// the solver
320 virtual void addStateConstraints(ProgramStateRef State) const {
321 // TODO: Don't add all the constraints, only the relevant ones
322 auto CZ = State->get<ConstraintSMT>();
323 auto I = CZ.begin(), IE = CZ.end();
324
325 // Construct the logical AND of all the constraints
326 if (I != IE) {
327 std::vector<llvm::SMTExprRef> ASTs;
328
329 llvm::SMTExprRef Constraint = I++->second;
330 while (I != IE) {
331 Constraint = Solver->mkAnd(Constraint, I++->second);
332 }
333
334 Solver->addConstraint(Constraint);
335 }
336 }
337
338 // Generate and check a Z3 model, using the given constraint.
340 const llvm::SMTExprRef &Exp) const {
341 ProgramStateRef NewState =
342 State->add<ConstraintSMT>(std::make_pair(Sym, Exp));
343
344 llvm::FoldingSetNodeID ID;
345 NewState->get<ConstraintSMT>().Profile(ID);
346
347 unsigned hash = ID.ComputeHash();
348 auto I = Cached.find(hash);
349 if (I != Cached.end())
350 return I->second;
351
352 Solver->reset();
353 addStateConstraints(NewState);
354
355 std::optional<bool> res = Solver->check();
356 if (!res)
357 Cached[hash] = ConditionTruthVal();
358 else
359 Cached[hash] = ConditionTruthVal(*res);
360
361 return Cached[hash];
362 }
363
364 // Cache the result of an SMT query (true, false, unknown). The key is the
365 // hash of the constraints in a state
366 mutable llvm::DenseMap<unsigned, ConditionTruthVal> Cached;
367}; // end class SMTConstraintManager
368
369} // namespace ento
370} // namespace clang
371
372#endif
static char ID
Definition: Arena.cpp:183
#define X(type, name)
Definition: Value.h:144
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a type named NameTy.
llvm::ImmutableSet< std::pair< clang::ento::SymbolRef, const llvm::SMTExpr * > > ConstraintSMTType
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:188
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
Definition: ASTContext.h:2482
const TargetInfo & getTargetInfo() const
Definition: ASTContext.h:799
A (possibly-)qualified type.
Definition: Type.h:929
Exposes information about the current target.
Definition: TargetInfo.h:220
const llvm::fltSemantics & getLongDoubleFormat() const
Definition: TargetInfo.h:788
bool isVoidType() const
Definition: Type.h:8510
bool isBooleanType() const
Definition: Type.h:8638
bool isSignedIntegerOrEnumerationType() const
Determines whether this is an integer type that is signed or an enumeration types whose underlying ty...
Definition: Type.cpp:2201
bool isComplexType() const
isComplexType() does not include complex integers (a GCC extension).
Definition: Type.cpp:710
bool isSpecificBuiltinType(unsigned K) const
Test for a particular builtin type.
Definition: Type.h:8479
bool isComplexIntegerType() const
Definition: Type.cpp:716
bool isRealFloatingType() const
Floating point categories.
Definition: Type.cpp:2300
LLVM_ATTRIBUTE_RETURNS_NONNULL const APSInt * get() const
Definition: APSIntPtr.h:36
std::optional< APSIntPtr > evalAPSInt(BinaryOperator::Opcode Op, const llvm::APSInt &V1, const llvm::APSInt &V2)
APSIntPtr Convert(const llvm::APSInt &To, const llvm::APSInt &From)
Convert - Create a new persistent APSInt with the same value as 'From' but with the bitwidth and sign...
Template implementation for all binary symbolic expressions.
Represents a symbolic expression involving a binary operator.
bool isConstrainedFalse() const
Return true if the constraint is perfectly constrained to 'false'.
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
SMTConstraintManager(clang::ento::ExprEngine *EE, clang::ento::SValBuilder &SB)
virtual void addStateConstraints(ProgramStateRef State) const
Given a program state, construct the logical conjunction and add it to the solver.
bool canReasonAbout(SVal X) const override
canReasonAbout - Not all ConstraintManagers can accurately reason about all SVal values.
ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, const llvm::APSInt &To, bool InRange) override
Given a symbolic expression within the range [From, To], assume that it is true/false and generate th...
LLVM_DUMP_METHOD void dump() const
Dumps SMT formula.
ConditionTruthVal checkModel(ProgramStateRef State, SymbolRef Sym, const llvm::SMTExprRef &Exp) const
const llvm::APSInt * getSymVal(ProgramStateRef State, SymbolRef Sym) const override
If a symbol is perfectly constrained to a constant, attempt to return the concrete value.
ProgramStateRef removeDeadBindings(ProgramStateRef State, SymbolReaper &SymReaper) override
Scan all symbols referenced by the constraints.
ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override
Returns whether or not a symbol is known to be null ("true"), known to be non-null ("false"),...
ProgramStateRef assumeSym(ProgramStateRef State, SymbolRef Sym, bool Assumption) override
Given a symbolic expression that can be reasoned about, assume that it is true/false and generate the...
llvm::DenseMap< unsigned, ConditionTruthVal > Cached
virtual ~SMTConstraintManager()=default
virtual ProgramStateRef assumeExpr(ProgramStateRef State, SymbolRef Sym, const llvm::SMTExprRef &Exp)
void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL="\n", unsigned int Space=0, bool IsDot=false) const override
ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym, bool Assumption) override
Given a symbolic expression that cannot be reasoned about, assume that it is zero/nonzero and add it ...
bool haveEqualConstraints(ProgramStateRef S1, ProgramStateRef S2) const override
static llvm::SMTExprRef getZeroExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, const llvm::SMTExprRef &Exp, QualType Ty, bool Assumption)
Definition: SMTConv.h:501
static llvm::SMTExprRef getRangeExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, SymbolRef Sym, const llvm::APSInt &From, const llvm::APSInt &To, bool InRange)
Definition: SMTConv.h:532
static llvm::SMTExprRef getExpr(llvm::SMTSolverRef &Solver, ASTContext &Ctx, SymbolRef Sym, QualType *RetTy=nullptr, bool *hasComparison=nullptr)
Definition: SMTConv.h:489
static llvm::SMTExprRef fromData(llvm::SMTSolverRef &Solver, ASTContext &Ctx, const SymbolData *Sym)
Construct an SMTSolverRef from a SymbolData.
Definition: SMTConv.h:323
static llvm::SMTExprRef fromBinOp(llvm::SMTSolverRef &Solver, const llvm::SMTExprRef &LHS, const BinaryOperator::Opcode Op, const llvm::SMTExprRef &RHS, bool isSigned)
Construct an SMTSolverRef from a binary operator.
Definition: SMTConv.h:90
static std::pair< llvm::APSInt, QualType > fixAPSInt(ASTContext &Ctx, const llvm::APSInt &Int)
Definition: SMTConv.h:578
DefinedSVal makeSymbolVal(SymbolRef Sym)
Make an SVal that represents the given symbol.
Definition: SValBuilder.h:397
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition: SVals.h:56
BasicValueFactory & getBasicVals() const
Symbolic value.
Definition: SymExpr.h:30
virtual QualType getType() const =0
Represents a cast expression.
A symbol representing data which can be stored in a memory location (region).
Definition: SymExpr.h:119
A class responsible for cleaning up unused symbols.
bool isDead(SymbolRef sym)
Returns whether or not a symbol has been confirmed dead.
Represents symbolic expression that isn't a location.
Definition: SVals.h:279
Defines the clang::TargetInfo interface.
The JSON file list parser is used to communicate input to InstallAPI.