diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp index 1a38be9c1374f..525ab188b01b8 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -157,12 +157,25 @@ static Value &widenDistinctValues(QualType Type, Value &Prev, Environment &CurrentEnv, Environment::ValueModel &Model) { // Boolean-model widening. - if (isa(&Prev)) { - assert(isa(Current)); - // Widen to Top, because we know they are different values. If previous was - // already Top, re-use that to (implicitly) indicate that no change occured. + if (auto *PrevBool = dyn_cast(&Prev)) { + // If previous value was already Top, re-use that to (implicitly) indicate + // that no change occurred. if (isa(Prev)) return Prev; + + // We may need to widen to Top, but before we do so, check whether both + // values are implied to be either true or false in the current environment. + // In that case, we can simply return a literal instead. + auto &CurBool = cast(Current); + bool TruePrev = PrevEnv.proves(PrevBool->formula()); + bool TrueCur = CurrentEnv.proves(CurBool.formula()); + if (TruePrev && TrueCur) + return CurrentEnv.getBoolLiteralValue(true); + if (!TruePrev && !TrueCur && + PrevEnv.proves(PrevEnv.arena().makeNot(PrevBool->formula())) && + CurrentEnv.proves(CurrentEnv.arena().makeNot(CurBool.formula()))) + return CurrentEnv.getBoolLiteralValue(false); + return CurrentEnv.makeTopBoolValue(); } diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp index ade0d202ced2f..8da55953a3298 100644 --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -4167,6 +4167,34 @@ TEST(TransferTest, LoopWithShortCircuitedConditionConverges) { ASSERT_THAT_ERROR(checkDataflowWithNoopAnalysis(Code), llvm::Succeeded()); } +TEST(TransferTest, LoopCanProveInvariantForBoolean) { + // Check that we can prove `b` is always false in the loop. + // This test exercises the logic in `widenDistinctValues()` that preserves + // information if the boolean can be proved to be either true or false in both + // the previous and current iteration. + std::string Code = R"cc( + int return_int(); + void target() { + bool b = return_int() == 0; + if (b) return; + while (true) { + b; + // [[p]] + b = return_int() == 0; + if (b) return; + } + } + )cc"; + runDataflow( + Code, + [](const llvm::StringMap> &Results, + ASTContext &ASTCtx) { + const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); + auto &BVal = getValueForDecl(ASTCtx, Env, "b"); + EXPECT_TRUE(Env.proves(Env.arena().makeNot(BVal.formula()))); + }); +} + TEST(TransferTest, DoesNotCrashOnUnionThisExpr) { std::string Code = R"( union Union {