Skip to content

Commit

Permalink
[dataflow] Remove deprecated BoolValue flow condition accessors
Browse files Browse the repository at this point in the history
Use the Formula versions instead, now.

Differential Revision: https://reviews.llvm.org/D155152
  • Loading branch information
sam-mccall committed Jul 13, 2023
1 parent 474e37c commit 6272226
Show file tree
Hide file tree
Showing 10 changed files with 269 additions and 253 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -544,15 +544,11 @@ class Environment {

/// Record a fact that must be true if this point in the program is reached.
void addToFlowCondition(const Formula &);
/// Deprecated: Use Formula version instead.
void addToFlowCondition(BoolValue &Val);

/// Returns true if the formula is always true when this point is reached.
/// Returns false if the formula may be false, or if the flow condition isn't
/// sufficiently precise to prove that it is true.
bool flowConditionImplies(const Formula &) const;
/// Deprecated: Use Formula version instead.
bool flowConditionImplies(BoolValue &Val) const;

/// Returns the `DeclContext` of the block being analysed, if any. Otherwise,
/// returns null.
Expand Down
6 changes: 0 additions & 6 deletions clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -894,16 +894,10 @@ const StorageLocation &Environment::skip(const StorageLocation &Loc,
void Environment::addToFlowCondition(const Formula &Val) {
DACtx->addFlowConditionConstraint(FlowConditionToken, Val);
}
void Environment::addToFlowCondition(BoolValue &Val) {
addToFlowCondition(Val.formula());
}

bool Environment::flowConditionImplies(const Formula &Val) const {
return DACtx->flowConditionImplies(FlowConditionToken, Val);
}
bool Environment::flowConditionImplies(BoolValue &Val) const {
return flowConditionImplies(Val.formula());
}

void Environment::dump(raw_ostream &OS) const {
// FIXME: add printing for remaining fields and allow caller to decide what
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ bool ChromiumCheckModel::transfer(const CFGElement &Element, Environment &Env) {
if (const auto *M = dyn_cast<CXXMethodDecl>(Call->getDirectCallee())) {
if (isCheckLikeMethod(CheckDecls, *M)) {
// Mark this branch as unreachable.
Env.addToFlowCondition(Env.getBoolLiteralValue(false));
Env.addToFlowCondition(Env.arena().makeLiteral(false));
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
#include "clang/Analysis/FlowSensitive/Formula.h"
#include "clang/Analysis/FlowSensitive/NoopLattice.h"
#include "clang/Analysis/FlowSensitive/StorageLocation.h"
#include "clang/Analysis/FlowSensitive/Value.h"
Expand Down Expand Up @@ -234,17 +235,17 @@ auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
hasArgument(1, rhs_arg_matcher));
}

/// Ensures that `Expr` is mapped to a `BoolValue` and returns it.
BoolValue &forceBoolValue(Environment &Env, const Expr &Expr) {
/// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula.
const Formula &forceBoolValue(Environment &Env, const Expr &Expr) {
auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr, SkipPast::None));
if (Value != nullptr)
return *Value;
return Value->formula();

auto &Loc = Env.createStorageLocation(Expr);
Value = &Env.makeAtomicBoolValue();
Env.setValue(Loc, *Value);
Env.setStorageLocation(Expr, Loc);
return *Value;
return Value->formula();
}

/// Sets `HasValueVal` as the symbolic value that represents the "has_value"
Expand Down Expand Up @@ -421,15 +422,16 @@ bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) {
auto *HasValueVal =
cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
return HasValueVal != nullptr &&
Env.flowConditionImplies(Env.makeNot(*HasValueVal));
Env.flowConditionImplies(Env.arena().makeNot(HasValueVal->formula()));
}

/// Returns true if and only if `OptionalVal` is initialized and known to be
/// non-empty in `Env`.
bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) {
auto *HasValueVal =
cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal);
return HasValueVal != nullptr &&
Env.flowConditionImplies(HasValueVal->formula());
}

Value *getValueBehindPossiblePointer(const Expr &E, const Environment &Env) {
Expand Down Expand Up @@ -485,12 +487,11 @@ void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,

/// `ModelPred` builds a logical formula relating the predicate in
/// `ValueOrPredExpr` to the optional's `has_value` property.
void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
const MatchFinder::MatchResult &Result,
LatticeTransferState &State,
BoolValue &(*ModelPred)(Environment &Env,
BoolValue &ExprVal,
BoolValue &HasValueVal)) {
void transferValueOrImpl(
const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result,
LatticeTransferState &State,
const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal,
const Formula &HasValueVal)) {
auto &Env = State.Env;

const auto *ObjectArgumentExpr =
Expand All @@ -502,37 +503,39 @@ void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
if (HasValueVal == nullptr)
return;

Env.addToFlowCondition(
ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), *HasValueVal));
Env.addToFlowCondition(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),
HasValueVal->formula()));
}

void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
const MatchFinder::MatchResult &Result,
LatticeTransferState &State) {
return transferValueOrImpl(ComparisonExpr, Result, State,
[](Environment &Env, BoolValue &ExprVal,
BoolValue &HasValueVal) -> BoolValue & {
[](Environment &Env, const Formula &ExprVal,
const Formula &HasValueVal) -> const Formula & {
auto &A = Env.arena();
// If the result is *not* empty, then we know the
// optional must have been holding a value. If
// `ExprVal` is true, though, we don't learn
// anything definite about `has_value`, so we
// don't add any corresponding implications to
// the flow condition.
return Env.makeImplication(Env.makeNot(ExprVal),
HasValueVal);
return A.makeImplies(A.makeNot(ExprVal),
HasValueVal);
});
}

void transferValueOrNotEqX(const Expr *ComparisonExpr,
const MatchFinder::MatchResult &Result,
LatticeTransferState &State) {
transferValueOrImpl(ComparisonExpr, Result, State,
[](Environment &Env, BoolValue &ExprVal,
BoolValue &HasValueVal) -> BoolValue & {
[](Environment &Env, const Formula &ExprVal,
const Formula &HasValueVal) -> const Formula & {
auto &A = Env.arena();
// We know that if `(opt.value_or(X) != X)` then
// `opt.hasValue()`, even without knowing further
// details about the contents of `opt`.
return Env.makeImplication(ExprVal, HasValueVal);
return A.makeImplies(ExprVal, HasValueVal);
});
}

Expand Down Expand Up @@ -701,8 +704,8 @@ void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
State.Env.setValue(*LocRet, *ValArg);
}

BoolValue &evaluateEquality(Environment &Env, BoolValue &EqVal, BoolValue &LHS,
BoolValue &RHS) {
const Formula &evaluateEquality(Arena &A, const Formula &EqVal,
const Formula &LHS, const Formula &RHS) {
// Logically, an optional<T> object is composed of two values - a `has_value`
// bit and a value of type T. Equality of optional objects compares both
// values. Therefore, merely comparing the `has_value` bits isn't sufficient:
Expand All @@ -717,37 +720,38 @@ BoolValue &evaluateEquality(Environment &Env, BoolValue &EqVal, BoolValue &LHS,
// b) (!LHS & !RHS) => EqVal
// If neither is set, then they are equal.
// We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
return Env.makeAnd(
Env.makeImplication(
EqVal, Env.makeOr(Env.makeAnd(LHS, RHS),
Env.makeAnd(Env.makeNot(LHS), Env.makeNot(RHS)))),
Env.makeImplication(Env.makeNot(EqVal), Env.makeOr(LHS, RHS)));
return A.makeAnd(
A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),
A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),
A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
}

void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
const MatchFinder::MatchResult &,
LatticeTransferState &State) {
Environment &Env = State.Env;
auto &A = Env.arena();
auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
if (auto *LHasVal = getHasValue(
Env, Env.getValue(*CmpExpr->getArg(0), SkipPast::Reference)))
if (auto *RHasVal = getHasValue(
Env, Env.getValue(*CmpExpr->getArg(1), SkipPast::Reference))) {
if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
CmpValue = &State.Env.makeNot(*CmpValue);
Env.addToFlowCondition(
evaluateEquality(Env, *CmpValue, *LHasVal, *RHasVal));
CmpValue = &A.makeNot(*CmpValue);
Env.addToFlowCondition(evaluateEquality(A, *CmpValue, LHasVal->formula(),
RHasVal->formula()));
}
}

void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
const clang::Expr *E, Environment &Env) {
auto &A = Env.arena();
auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
if (auto *HasVal = getHasValue(Env, Env.getValue(*E, SkipPast::Reference))) {
if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
CmpValue = &Env.makeNot(*CmpValue);
Env.addToFlowCondition(evaluateEquality(Env, *CmpValue, *HasVal,
Env.getBoolLiteralValue(true)));
CmpValue = &A.makeNot(*CmpValue);
Env.addToFlowCondition(
evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true)));
}
}

Expand Down Expand Up @@ -929,7 +933,7 @@ std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *ObjectExpr,
if (auto *OptionalVal = getValueBehindPossiblePointer(*ObjectExpr, Env)) {
auto *Prop = OptionalVal->getProperty("has_value");
if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
if (Env.flowConditionImplies(*HasValueVal))
if (Env.flowConditionImplies(HasValueVal->formula()))
return {};
}
}
Expand Down Expand Up @@ -1015,13 +1019,14 @@ bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1,
bool MustNonEmpty1 = isNonEmptyOptional(Val1, Env1);
bool MustNonEmpty2 = isNonEmptyOptional(Val2, Env2);
if (MustNonEmpty1 && MustNonEmpty2)
MergedEnv.addToFlowCondition(HasValueVal);
MergedEnv.addToFlowCondition(HasValueVal.formula());
else if (
// Only make the costly calls to `isEmptyOptional` if we got "unknown"
// (false) for both calls to `isNonEmptyOptional`.
!MustNonEmpty1 && !MustNonEmpty2 && isEmptyOptional(Val1, Env1) &&
isEmptyOptional(Val2, Env2))
MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal));
MergedEnv.addToFlowCondition(
MergedEnv.arena().makeNot(HasValueVal.formula()));
setHasValue(MergedVal, HasValueVal);
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class TerminatorVisitor
ConditionValue = false;
}

Env.addToFlowCondition(*Val);
Env.addToFlowCondition(Val->formula());
return {&Cond, ConditionValue};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ TEST(ChromiumCheckModelTest, CheckSuccessImpliesConditionHolds) {

auto *FooVal = cast<BoolValue>(Env.getValue(*FooDecl));

EXPECT_TRUE(Env.flowConditionImplies(*FooVal));
EXPECT_TRUE(Env.flowConditionImplies(FooVal->formula()));
};

std::string Code = R"(
Expand Down Expand Up @@ -191,7 +191,7 @@ TEST(ChromiumCheckModelTest, UnrelatedCheckIgnored) {

auto *FooVal = cast<BoolValue>(Env.getValue(*FooDecl));

EXPECT_FALSE(Env.flowConditionImplies(*FooVal));
EXPECT_FALSE(Env.flowConditionImplies(FooVal->formula()));
};

std::string Code = R"(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ namespace {
using namespace clang;
using namespace dataflow;
using ::clang::dataflow::test::getFieldValue;
using ::testing::ElementsAre;
using ::testing::NotNull;
using ::testing::Pair;

class EnvironmentTest : public ::testing::Test {
protected:
Expand All @@ -38,17 +36,18 @@ class EnvironmentTest : public ::testing::Test {

TEST_F(EnvironmentTest, FlowCondition) {
Environment Env(DAContext);
auto &A = Env.arena();

EXPECT_TRUE(Env.flowConditionImplies(Env.getBoolLiteralValue(true)));
EXPECT_FALSE(Env.flowConditionImplies(Env.getBoolLiteralValue(false)));
EXPECT_TRUE(Env.flowConditionImplies(A.makeLiteral(true)));
EXPECT_FALSE(Env.flowConditionImplies(A.makeLiteral(false)));

auto &X = Env.makeAtomicBoolValue();
auto &X = A.makeAtomRef(A.makeAtom());
EXPECT_FALSE(Env.flowConditionImplies(X));

Env.addToFlowCondition(X);
EXPECT_TRUE(Env.flowConditionImplies(X));

auto &NotX = Env.makeNot(X);
auto &NotX = A.makeNot(X);
EXPECT_FALSE(Env.flowConditionImplies(NotX));
}

Expand Down

0 comments on commit 6272226

Please sign in to comment.