diff --git a/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h b/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h --- a/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h +++ b/clang/include/clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h @@ -47,6 +47,9 @@ : MaxIterations(WorkLimit) {} Result solve(llvm::ArrayRef Vals) override; + + // The solver reached its maximum number of iterations. + bool reachedLimit() const { return MaxIterations == 0; } }; } // namespace dataflow diff --git a/clang/unittests/Analysis/FlowSensitive/SolverTest.cpp b/clang/unittests/Analysis/FlowSensitive/SolverTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/SolverTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/SolverTest.cpp @@ -350,4 +350,23 @@ EXPECT_EQ(solver.solve({A, B}).getStatus(), Solver::Result::Status::TimedOut); } +TEST(SolverTest, ReachedLimitsReflectsTimeouts) { + WatchedLiteralsSolver solver(10); + ConstraintContext Ctx; + auto X = Ctx.atom(); + auto Y = Ctx.atom(); + auto Z = Ctx.atom(); + auto W = Ctx.atom(); + + // !(X v Y) <=> !X ^ !Y + auto A = Ctx.iff(Ctx.neg(Ctx.disj(X, Y)), Ctx.conj(Ctx.neg(X), Ctx.neg(Y))); + + // !(Z ^ W) <=> !Z v !W + auto B = Ctx.iff(Ctx.neg(Ctx.conj(Z, W)), Ctx.disj(Ctx.neg(Z), Ctx.neg(W))); + + // A ^ B + ASSERT_EQ(solver.solve({A, B}).getStatus(), Solver::Result::Status::TimedOut); + EXPECT_TRUE(solver.reachedLimit()); +} + } // namespace