@@ -71,14 +71,6 @@ class StdLibraryFunctionsChecker : public Checker<check::PostCall, eval::Call> {
7171 // / to us. If he doesn't, he performs additional invalidations.
7272 enum InvalidationKind { NoEvalCall, EvalCallAsPure };
7373
74- // / A pair of ValueRangeKind and IntRangeVector would describe a range
75- // / imposed on a particular argument or return value symbol.
76- // /
77- // / Given a range, should the argument stay inside or outside this range?
78- // / The special `ComparesToArgument' value indicates that we should
79- // / impose a constraint that involves other argument or return value symbols.
80- enum ValueRangeKind { OutOfRange, WithinRange, ComparesToArgument };
81-
8274 // The universal integral type to use in value range descriptions.
8375 // Unsigned to make sure overflows are well-defined.
8476 typedef uint64_t RangeInt;
@@ -93,75 +85,87 @@ class StdLibraryFunctionsChecker : public Checker<check::PostCall, eval::Call> {
9385 // / ArgNo in CallExpr and CallEvent is defined as Unsigned, but
9486 // / obviously uint32_t should be enough for all practical purposes.
9587 typedef uint32_t ArgNo;
96- static const ArgNo Ret = std::numeric_limits<ArgNo>::max();
97-
98- // / Incapsulates a single range on a single symbol within a branch.
99- class ValueRange {
100- ArgNo ArgN; // Argument to which we apply the range.
101- ValueRangeKind Kind; // Kind of range definition.
102- IntRangeVector Args; // Polymorphic arguments.
88+ static const ArgNo Ret;
10389
90+ // / Polymorphic base class that represents a constraint on a given argument
91+ // / (or return value) of a function. Derived classes implement different kind
92+ // / of constraints, e.g range constraints or correlation between two
93+ // / arguments.
94+ class ValueConstraint {
10495 public:
105- ValueRange (ArgNo ArgN, ValueRangeKind Kind, const IntRangeVector &Args)
106- : ArgN(ArgN), Kind(Kind), Args(Args) {}
107-
96+ ValueConstraint (ArgNo ArgN) : ArgN(ArgN) {}
97+ virtual ~ValueConstraint () {}
98+ // / Apply the effects of the constraint on the given program state. If null
99+ // / is returned then the constraint is not feasible.
100+ virtual ProgramStateRef apply (ProgramStateRef State, const CallEvent &Call,
101+ const Summary &Summary) const = 0;
108102 ArgNo getArgNo () const { return ArgN; }
109- ValueRangeKind getKind () const { return Kind; }
110-
111- BinaryOperator::Opcode getOpcode () const {
112- assert (Kind == ComparesToArgument);
113- assert (Args.size () == 1 );
114- BinaryOperator::Opcode Op =
115- static_cast <BinaryOperator::Opcode>(Args[0 ].first );
116- assert (BinaryOperator::isComparisonOp (Op) &&
117- " Only comparison ops are supported for ComparesToArgument" );
118- return Op;
119- }
120103
121- ArgNo getOtherArgNo () const {
122- assert (Kind == ComparesToArgument);
123- assert (Args.size () == 1 );
124- return static_cast <ArgNo>(Args[0 ].second );
125- }
104+ protected:
105+ ArgNo ArgN; // Argument to which we apply the constraint.
106+ };
107+
108+ // / Given a range, should the argument stay inside or outside this range?
109+ enum RangeKind { OutOfRange, WithinRange };
110+
111+ // / Encapsulates a single range on a single symbol within a branch.
112+ class RangeConstraint : public ValueConstraint {
113+ RangeKind Kind; // Kind of range definition.
114+ IntRangeVector Args; // Polymorphic arguments.
115+
116+ public:
117+ RangeConstraint (ArgNo ArgN, RangeKind Kind, const IntRangeVector &Args)
118+ : ValueConstraint(ArgN), Kind(Kind), Args(Args) {}
126119
127120 const IntRangeVector &getRanges () const {
128- assert (Kind != ComparesToArgument);
129121 return Args;
130122 }
131123
132- // We avoid creating a virtual apply() method because
133- // it makes initializer lists harder to write.
134124 private:
135125 ProgramStateRef applyAsOutOfRange (ProgramStateRef State,
136126 const CallEvent &Call,
137127 const Summary &Summary) const ;
138128 ProgramStateRef applyAsWithinRange (ProgramStateRef State,
139129 const CallEvent &Call,
140130 const Summary &Summary) const ;
141- ProgramStateRef applyAsComparesToArgument (ProgramStateRef State,
142- const CallEvent &Call,
143- const Summary &Summary) const ;
144-
145131 public:
146132 ProgramStateRef apply (ProgramStateRef State, const CallEvent &Call,
147- const Summary &Summary) const {
133+ const Summary &Summary) const override {
148134 switch (Kind) {
149135 case OutOfRange:
150136 return applyAsOutOfRange (State, Call, Summary);
151137 case WithinRange:
152138 return applyAsWithinRange (State, Call, Summary);
153- case ComparesToArgument:
154- return applyAsComparesToArgument (State, Call, Summary);
155139 }
156- llvm_unreachable (" Unknown ValueRange kind!" );
140+ llvm_unreachable (" Unknown range kind!" );
157141 }
158142 };
159143
160- // / The complete list of ranges that defines a single branch.
161- typedef std::vector<ValueRange> ValueRangeSet;
144+ class ComparisonConstraint : public ValueConstraint {
145+ BinaryOperator::Opcode Opcode;
146+ ArgNo OtherArgN;
147+
148+ public:
149+ ComparisonConstraint (ArgNo ArgN, BinaryOperator::Opcode Opcode,
150+ ArgNo OtherArgN)
151+ : ValueConstraint(ArgN), Opcode(Opcode), OtherArgN(OtherArgN) {}
152+ ArgNo getOtherArgNo () const { return OtherArgN; }
153+ BinaryOperator::Opcode getOpcode () const { return Opcode; }
154+ ProgramStateRef apply (ProgramStateRef State, const CallEvent &Call,
155+ const Summary &Summary) const override ;
156+ };
157+
158+ // Pointer to the ValueConstraint. We need a copyable, polymorphic and
159+ // default initialize able type (vector needs that). A raw pointer was good,
160+ // however, we cannot default initialize that. unique_ptr makes the Summary
161+ // class non-copyable, therefore not an option. Releasing the copyability
162+ // requirement would render the initialization of the Summary map infeasible.
163+ using ValueConstraintPtr = std::shared_ptr<ValueConstraint>;
164+ // / The complete list of constraints that defines a single branch.
165+ typedef std::vector<ValueConstraintPtr> ConstraintSet;
162166
163167 using ArgTypes = std::vector<QualType>;
164- using Ranges = std::vector<ValueRangeSet >;
168+ using Cases = std::vector<ConstraintSet >;
165169
166170 // / Includes information about function prototype (which is necessary to
167171 // / ensure we're modeling the right function and casting values properly),
@@ -171,14 +175,14 @@ class StdLibraryFunctionsChecker : public Checker<check::PostCall, eval::Call> {
171175 const ArgTypes ArgTys;
172176 const QualType RetTy;
173177 const InvalidationKind InvalidationKd;
174- Ranges Cases;
175- ValueRangeSet ArgConstraints;
178+ Cases CaseConstraints ;
179+ ConstraintSet ArgConstraints;
176180
177181 Summary (ArgTypes ArgTys, QualType RetTy, InvalidationKind InvalidationKd)
178182 : ArgTys(ArgTys), RetTy(RetTy), InvalidationKd(InvalidationKd) {}
179183
180- Summary &Case (ValueRangeSet VRS ) {
181- Cases .push_back (VRS );
184+ Summary &Case (ConstraintSet&& CS ) {
185+ CaseConstraints .push_back (std::move (CS) );
182186 return *this ;
183187 }
184188
@@ -244,9 +248,13 @@ class StdLibraryFunctionsChecker : public Checker<check::PostCall, eval::Call> {
244248
245249 void initFunctionSummaries (CheckerContext &C) const ;
246250};
251+
252+ const StdLibraryFunctionsChecker::ArgNo StdLibraryFunctionsChecker::Ret =
253+ std::numeric_limits<ArgNo>::max();
254+
247255} // end of anonymous namespace
248256
249- ProgramStateRef StdLibraryFunctionsChecker::ValueRange ::applyAsOutOfRange (
257+ ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint ::applyAsOutOfRange (
250258 ProgramStateRef State, const CallEvent &Call,
251259 const Summary &Summary) const {
252260
@@ -273,7 +281,7 @@ ProgramStateRef StdLibraryFunctionsChecker::ValueRange::applyAsOutOfRange(
273281 return State;
274282}
275283
276- ProgramStateRef StdLibraryFunctionsChecker::ValueRange ::applyAsWithinRange (
284+ ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint ::applyAsWithinRange (
277285 ProgramStateRef State, const CallEvent &Call,
278286 const Summary &Summary) const {
279287
@@ -330,8 +338,7 @@ ProgramStateRef StdLibraryFunctionsChecker::ValueRange::applyAsWithinRange(
330338 return State;
331339}
332340
333- ProgramStateRef
334- StdLibraryFunctionsChecker::ValueRange::applyAsComparesToArgument (
341+ ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply (
335342 ProgramStateRef State, const CallEvent &Call,
336343 const Summary &Summary) const {
337344
@@ -367,15 +374,15 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
367374 if (!FoundSummary)
368375 return ;
369376
370- // Now apply ranges .
377+ // Now apply the constraints .
371378 const Summary &Summary = *FoundSummary;
372379 ProgramStateRef State = C.getState ();
373380
374381 // Apply case/branch specifications.
375- for (const auto &VRS : Summary.Cases ) {
382+ for (const auto &VRS : Summary.CaseConstraints ) {
376383 ProgramStateRef NewState = State;
377384 for (const auto &VR: VRS) {
378- NewState = VR. apply (NewState, Call, Summary);
385+ NewState = VR-> apply (NewState, Call, Summary);
379386 if (!NewState)
380387 break ;
381388 }
@@ -555,24 +562,26 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
555562 // Please update the list of functions in the header after editing!
556563 //
557564
558- // Below are helper functions to create the summaries.
559- auto ArgumentCondition = [](ArgNo ArgN, ValueRangeKind Kind,
560- IntRangeVector Ranges) -> ValueRange {
561- ValueRange VR{ArgN, Kind, Ranges};
562- return VR;
563- };
564- auto ReturnValueCondition = [](ValueRangeKind Kind,
565- IntRangeVector Ranges) -> ValueRange {
566- ValueRange VR{Ret, Kind, Ranges};
567- return VR;
565+ // Below are helpers functions to create the summaries.
566+ auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind,
567+ IntRangeVector Ranges) {
568+ return std::make_shared<RangeConstraint>(ArgN, Kind, Ranges);
568569 };
570+ struct {
571+ auto operator ()(RangeKind Kind, IntRangeVector Ranges) {
572+ return std::make_shared<RangeConstraint>(Ret, Kind, Ranges);
573+ }
574+ auto operator ()(BinaryOperator::Opcode Op, ArgNo OtherArgN) {
575+ return std::make_shared<ComparisonConstraint>(Ret, Op, OtherArgN);
576+ }
577+ } ReturnValueCondition;
569578 auto Range = [](RangeInt b, RangeInt e) {
570579 return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}};
571580 };
572581 auto SingleValue = [](RangeInt v) {
573582 return IntRangeVector{std::pair<RangeInt, RangeInt>{v, v}};
574583 };
575- auto IsLessThan = [](ArgNo ArgN) { return IntRangeVector{{ BO_LE, ArgN}}; } ;
584+ auto LessThanOrEq = BO_LE;
576585
577586 using RetType = QualType;
578587
@@ -585,14 +594,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
585594 auto Read = [&](RetType R, RangeInt Max) {
586595 return Summary (ArgTypes{Irrelevant, Irrelevant, SizeTy}, RetType{R},
587596 NoEvalCall)
588- .Case ({ReturnValueCondition (ComparesToArgument, IsLessThan (2 )),
597+ .Case ({ReturnValueCondition (LessThanOrEq, ArgNo (2 )),
589598 ReturnValueCondition (WithinRange, Range (-1 , Max))});
590599 };
591600 auto Fread = [&]() {
592601 return Summary (ArgTypes{Irrelevant, Irrelevant, SizeTy, Irrelevant},
593602 RetType{SizeTy}, NoEvalCall)
594603 .Case ({
595- ReturnValueCondition (ComparesToArgument, IsLessThan (2 )),
604+ ReturnValueCondition (LessThanOrEq, ArgNo (2 )),
596605 });
597606 };
598607 auto Getline = [&](RetType R, RangeInt Max) {
0 commit comments