Skip to content

Commit 3fa27cc

Browse files
committed
Revert "[analyzer] Don't assume third iteration in loops (llvm#119388)"
This reverts commit bb27d5e. DO NOT MERGE, this is only for comparison.
1 parent a84a6f7 commit 3fa27cc

File tree

7 files changed

+44
-426
lines changed

7 files changed

+44
-426
lines changed

clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,14 +126,6 @@ class CoreEngine {
126126
ExplodedNode *generateCallExitBeginNode(ExplodedNode *N,
127127
const ReturnStmt *RS);
128128

129-
/// Helper function called by `HandleBranch()`. If the currently handled
130-
/// branch corresponds to a loop, this returns the number of already
131-
/// completed iterations in that loop, otherwise the return value is
132-
/// `std::nullopt`. Note that this counts _all_ earlier iterations, including
133-
/// ones that were performed within an earlier iteration of an outer loop.
134-
std::optional<unsigned> getCompletedIterationCount(const CFGBlock *B,
135-
ExplodedNode *Pred) const;
136-
137129
public:
138130
/// Construct a CoreEngine object to analyze the provided CFG.
139131
CoreEngine(ExprEngine &exprengine,

clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -321,14 +321,14 @@ class ExprEngine {
321321
NodeBuilderWithSinks &nodeBuilder,
322322
ExplodedNode *Pred);
323323

324-
/// ProcessBranch - Called by CoreEngine. Used to generate successor nodes by
325-
/// processing the 'effects' of a branch condition. If the branch condition
326-
/// is a loop condition, IterationsCompletedInLoop is the number of completed
327-
/// iterations (otherwise it's std::nullopt).
328-
void processBranch(const Stmt *Condition, NodeBuilderContext &BuilderCtx,
329-
ExplodedNode *Pred, ExplodedNodeSet &Dst,
330-
const CFGBlock *DstT, const CFGBlock *DstF,
331-
std::optional<unsigned> IterationsCompletedInLoop);
324+
/// ProcessBranch - Called by CoreEngine. Used to generate successor
325+
/// nodes by processing the 'effects' of a branch condition.
326+
void processBranch(const Stmt *Condition,
327+
NodeBuilderContext& BuilderCtx,
328+
ExplodedNode *Pred,
329+
ExplodedNodeSet &Dst,
330+
const CFGBlock *DstT,
331+
const CFGBlock *DstF);
332332

333333
/// Called by CoreEngine.
334334
/// Used to generate successor nodes for temporary destructors depending
@@ -592,8 +592,6 @@ class ExprEngine {
592592
void evalEagerlyAssumeBifurcation(ExplodedNodeSet &Dst, ExplodedNodeSet &Src,
593593
const Expr *Ex);
594594

595-
bool didEagerlyAssumeBifurcateAt(ProgramStateRef State, const Expr *Ex) const;
596-
597595
static std::pair<const ProgramPointTag *, const ProgramPointTag *>
598596
getEagerlyAssumeBifurcationTags();
599597

clang/lib/StaticAnalyzer/Core/CoreEngine.cpp

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,7 @@ void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
477477
NodeBuilderContext Ctx(*this, B, Pred);
478478
ExplodedNodeSet Dst;
479479
ExprEng.processBranch(Cond, Ctx, Pred, Dst, *(B->succ_begin()),
480-
*(B->succ_begin() + 1),
481-
getCompletedIterationCount(B, Pred));
480+
*(B->succ_begin() + 1));
482481
// Enqueue the new frontier onto the worklist.
483482
enqueue(Dst);
484483
}
@@ -625,30 +624,6 @@ ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N,
625624
return isNew ? Node : nullptr;
626625
}
627626

628-
std::optional<unsigned>
629-
CoreEngine::getCompletedIterationCount(const CFGBlock *B,
630-
ExplodedNode *Pred) const {
631-
const LocationContext *LC = Pred->getLocationContext();
632-
BlockCounter Counter = WList->getBlockCounter();
633-
unsigned BlockCount =
634-
Counter.getNumVisited(LC->getStackFrame(), B->getBlockID());
635-
636-
const Stmt *Term = B->getTerminatorStmt();
637-
if (isa<ForStmt, WhileStmt, CXXForRangeStmt>(Term)) {
638-
assert(BlockCount >= 1 &&
639-
"Block count of currently analyzed block must be >= 1");
640-
return BlockCount - 1;
641-
}
642-
if (isa<DoStmt>(Term)) {
643-
// In a do-while loop one iteration happens before the first evaluation of
644-
// the loop condition, so we don't subtract one.
645-
return BlockCount;
646-
}
647-
// ObjCForCollectionStmt is skipped intentionally because the current
648-
// application of the iteration counts is not relevant for it.
649-
return std::nullopt;
650-
}
651-
652627
void CoreEngine::enqueue(ExplodedNodeSet &Set) {
653628
for (const auto I : Set)
654629
WList->enqueue(I);

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp

Lines changed: 11 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -2776,10 +2776,12 @@ assumeCondition(const Stmt *Condition, ExplodedNode *N) {
27762776
return State->assume(V);
27772777
}
27782778

2779-
void ExprEngine::processBranch(
2780-
const Stmt *Condition, NodeBuilderContext &BldCtx, ExplodedNode *Pred,
2781-
ExplodedNodeSet &Dst, const CFGBlock *DstT, const CFGBlock *DstF,
2782-
std::optional<unsigned> IterationsCompletedInLoop) {
2779+
void ExprEngine::processBranch(const Stmt *Condition,
2780+
NodeBuilderContext& BldCtx,
2781+
ExplodedNode *Pred,
2782+
ExplodedNodeSet &Dst,
2783+
const CFGBlock *DstT,
2784+
const CFGBlock *DstF) {
27832785
assert((!Condition || !isa<CXXBindTemporaryExpr>(Condition)) &&
27842786
"CXXBindTemporaryExprs are handled by processBindTemporary.");
27852787
const LocationContext *LCtx = Pred->getLocationContext();
@@ -2822,63 +2824,11 @@ void ExprEngine::processBranch(
28222824
if (StTrue && StFalse)
28232825
assert(!isa<ObjCForCollectionStmt>(Condition));
28242826

2825-
// We want to ensure consistent behavior between `eagerly-assume=false`,
2826-
// when the state split is always performed by the `assumeCondition()`
2827-
// call within this function and `eagerly-assume=true` (the default), when
2828-
// some conditions (comparison operators, unary negation) can trigger a
2829-
// state split before this callback. There are some contrived corner cases
2830-
// that behave differently with and without `eagerly-assume`, but I don't
2831-
// know about an example that could plausibly appear in "real" code.
2832-
bool BothFeasible =
2833-
(StTrue && StFalse) ||
2834-
didEagerlyAssumeBifurcateAt(PrevState, dyn_cast<Expr>(Condition));
2835-
2836-
if (StTrue) {
2837-
// In a loop, if both branches are feasible (i.e. the analyzer doesn't
2838-
// understand the loop condition) and two iterations have already been
2839-
// completed, then don't assume a third iteration because it is a
2840-
// redundant execution path (unlikely to be different from earlier loop
2841-
// exits) and can cause false positives if e.g. the loop iterates over a
2842-
// two-element structure with an opaque condition.
2843-
//
2844-
// The iteration count "2" is hardcoded because it's the natural limit:
2845-
// * the fact that the programmer wrote a loop (and not just an `if`)
2846-
// implies that they thought that the loop body might be executed twice;
2847-
// * however, there are situations where the programmer knows that there
2848-
// are at most two iterations but writes a loop that appears to be
2849-
// generic, because there is no special syntax for "loop with at most
2850-
// two iterations". (This pattern is common in FFMPEG and appears in
2851-
// many other projects as well.)
2852-
bool CompletedTwoIterations = IterationsCompletedInLoop.value_or(0) >= 2;
2853-
bool SkipTrueBranch = BothFeasible && CompletedTwoIterations;
2854-
2855-
// FIXME: This "don't assume third iteration" heuristic partially
2856-
// conflicts with the widen-loop analysis option (which is off by
2857-
// default). If we intend to support and stabilize the loop widening,
2858-
// we must ensure that it 'plays nicely' with this logic.
2859-
if (!SkipTrueBranch || AMgr.options.ShouldWidenLoops)
2860-
Builder.generateNode(StTrue, true, PredN);
2861-
}
2862-
2863-
if (StFalse) {
2864-
// In a loop, if both branches are feasible (i.e. the analyzer doesn't
2865-
// understand the loop condition), we are before the first iteration and
2866-
// the analyzer option `assume-at-least-one-iteration` is set to `true`,
2867-
// then avoid creating the execution path where the loop is skipped.
2868-
//
2869-
// In some situations this "loop is skipped" execution path is an
2870-
// important corner case that may evade the notice of the developer and
2871-
// hide significant bugs -- however, there are also many situations where
2872-
// it's guaranteed that at least one iteration will happen (e.g. some
2873-
// data structure is always nonempty), but the analyzer cannot realize
2874-
// this and will produce false positives when it assumes that the loop is
2875-
// skipped.
2876-
bool BeforeFirstIteration = IterationsCompletedInLoop == std::optional{0};
2877-
bool SkipFalseBranch = BothFeasible && BeforeFirstIteration &&
2878-
AMgr.options.ShouldAssumeAtLeastOneIteration;
2879-
if (!SkipFalseBranch)
2880-
Builder.generateNode(StFalse, false, PredN);
2881-
}
2827+
if (StTrue)
2828+
Builder.generateNode(StTrue, true, PredN);
2829+
2830+
if (StFalse)
2831+
Builder.generateNode(StFalse, false, PredN);
28822832
}
28832833
currBldrCtx = nullptr;
28842834
}
@@ -3819,12 +3769,6 @@ ExprEngine::getEagerlyAssumeBifurcationTags() {
38193769
return std::make_pair(&TrueTag, &FalseTag);
38203770
}
38213771

3822-
/// If the last EagerlyAssume attempt was successful (i.e. the true and false
3823-
/// cases were both feasible), this state trait stores the expression where it
3824-
/// happened; otherwise this holds nullptr.
3825-
REGISTER_TRAIT_WITH_PROGRAMSTATE(LastEagerlyAssumeExprIfSuccessful,
3826-
const Expr *)
3827-
38283772
void ExprEngine::evalEagerlyAssumeBifurcation(ExplodedNodeSet &Dst,
38293773
ExplodedNodeSet &Src,
38303774
const Expr *Ex) {
@@ -3840,19 +3784,13 @@ void ExprEngine::evalEagerlyAssumeBifurcation(ExplodedNodeSet &Dst,
38403784
}
38413785

38423786
ProgramStateRef State = Pred->getState();
3843-
State = State->set<LastEagerlyAssumeExprIfSuccessful>(nullptr);
38443787
SVal V = State->getSVal(Ex, Pred->getLocationContext());
38453788
std::optional<nonloc::SymbolVal> SEV = V.getAs<nonloc::SymbolVal>();
38463789
if (SEV && SEV->isExpression()) {
38473790
const auto &[TrueTag, FalseTag] = getEagerlyAssumeBifurcationTags();
38483791

38493792
auto [StateTrue, StateFalse] = State->assume(*SEV);
38503793

3851-
if (StateTrue && StateFalse) {
3852-
StateTrue = StateTrue->set<LastEagerlyAssumeExprIfSuccessful>(Ex);
3853-
StateFalse = StateFalse->set<LastEagerlyAssumeExprIfSuccessful>(Ex);
3854-
}
3855-
38563794
// First assume that the condition is true.
38573795
if (StateTrue) {
38583796
SVal Val = svalBuilder.makeIntVal(1U, Ex->getType());
@@ -3870,11 +3808,6 @@ void ExprEngine::evalEagerlyAssumeBifurcation(ExplodedNodeSet &Dst,
38703808
}
38713809
}
38723810

3873-
bool ExprEngine::didEagerlyAssumeBifurcateAt(ProgramStateRef State,
3874-
const Expr *Ex) const {
3875-
return Ex && State->get<LastEagerlyAssumeExprIfSuccessful>() == Ex;
3876-
}
3877-
38783811
void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred,
38793812
ExplodedNodeSet &Dst) {
38803813
StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);

0 commit comments

Comments
 (0)