Skip to content

Commit 5bd643e

Browse files
authored
[clang][dataflow] Strengthen widening of boolean values. (#73484)
Before we widen to top, we now check if both values can be proved either true or false in their respective environments; if so, widening returns a true or false literal. The idea is that we avoid losing information if posssible. This patch includes a test that fails without this change to widening. This change does mean that we call the SAT solver in more places, but this seems acceptable given the additional precision we gain. In tests on an internal codebase, the number of SAT solver timeouts we observe with Crubit's nullability checker does increase by about 25%. They can be brought back to the previous level by doubling the SAT solver work limit.
1 parent edf6456 commit 5bd643e

File tree

2 files changed

+45
-4
lines changed

2 files changed

+45
-4
lines changed

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,25 @@ static Value &widenDistinctValues(QualType Type, Value &Prev,
157157
Environment &CurrentEnv,
158158
Environment::ValueModel &Model) {
159159
// Boolean-model widening.
160-
if (isa<BoolValue>(&Prev)) {
161-
assert(isa<BoolValue>(Current));
162-
// Widen to Top, because we know they are different values. If previous was
163-
// already Top, re-use that to (implicitly) indicate that no change occured.
160+
if (auto *PrevBool = dyn_cast<BoolValue>(&Prev)) {
161+
// If previous value was already Top, re-use that to (implicitly) indicate
162+
// that no change occurred.
164163
if (isa<TopBoolValue>(Prev))
165164
return Prev;
165+
166+
// We may need to widen to Top, but before we do so, check whether both
167+
// values are implied to be either true or false in the current environment.
168+
// In that case, we can simply return a literal instead.
169+
auto &CurBool = cast<BoolValue>(Current);
170+
bool TruePrev = PrevEnv.proves(PrevBool->formula());
171+
bool TrueCur = CurrentEnv.proves(CurBool.formula());
172+
if (TruePrev && TrueCur)
173+
return CurrentEnv.getBoolLiteralValue(true);
174+
if (!TruePrev && !TrueCur &&
175+
PrevEnv.proves(PrevEnv.arena().makeNot(PrevBool->formula())) &&
176+
CurrentEnv.proves(CurrentEnv.arena().makeNot(CurBool.formula())))
177+
return CurrentEnv.getBoolLiteralValue(false);
178+
166179
return CurrentEnv.makeTopBoolValue();
167180
}
168181

clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4167,6 +4167,34 @@ TEST(TransferTest, LoopWithShortCircuitedConditionConverges) {
41674167
ASSERT_THAT_ERROR(checkDataflowWithNoopAnalysis(Code), llvm::Succeeded());
41684168
}
41694169

4170+
TEST(TransferTest, LoopCanProveInvariantForBoolean) {
4171+
// Check that we can prove `b` is always false in the loop.
4172+
// This test exercises the logic in `widenDistinctValues()` that preserves
4173+
// information if the boolean can be proved to be either true or false in both
4174+
// the previous and current iteration.
4175+
std::string Code = R"cc(
4176+
int return_int();
4177+
void target() {
4178+
bool b = return_int() == 0;
4179+
if (b) return;
4180+
while (true) {
4181+
b;
4182+
// [[p]]
4183+
b = return_int() == 0;
4184+
if (b) return;
4185+
}
4186+
}
4187+
)cc";
4188+
runDataflow(
4189+
Code,
4190+
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4191+
ASTContext &ASTCtx) {
4192+
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
4193+
auto &BVal = getValueForDecl<BoolValue>(ASTCtx, Env, "b");
4194+
EXPECT_TRUE(Env.proves(Env.arena().makeNot(BVal.formula())));
4195+
});
4196+
}
4197+
41704198
TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
41714199
std::string Code = R"(
41724200
union Union {

0 commit comments

Comments
 (0)