[clang][dataflow] Add support for (built-in) (in)equality operators

Adds logical interpretation of built-in equality operators, `==` and `!=`.s

Differential Revision: https://reviews.llvm.org/D122830
This commit is contained in:
Yitzhak Mandelbaum 2022-03-25 20:01:18 +00:00
parent c45975cbf9
commit ef1e1b3106
2 changed files with 100 additions and 1 deletions

View File

@ -21,6 +21,7 @@
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
#include "clang/Analysis/FlowSensitive/Value.h"
#include "clang/Basic/OperatorKinds.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
@ -37,6 +38,26 @@ static const Expr *skipExprWithCleanups(const Expr *E) {
return E;
}
static BoolValue &evaluateBooleanEquality(const Expr &LHS, const Expr &RHS,
Environment &Env) {
// Equality of booleans involves implicit integral casts. Ignore these casts
// for now and focus on the values associated with the wrapped expressions.
// FIXME: Consider changing this once the framework offers better support for
// integral casts.
const Expr *LHSNorm = LHS.IgnoreCasts();
const Expr *RHSNorm = RHS.IgnoreCasts();
assert(LHSNorm != nullptr);
assert(RHSNorm != nullptr);
if (auto *LHSValue = dyn_cast_or_null<BoolValue>(
Env.getValue(*LHSNorm, SkipPast::Reference)))
if (auto *RHSValue = dyn_cast_or_null<BoolValue>(
Env.getValue(*RHSNorm, SkipPast::Reference)))
return Env.makeIff(*LHSValue, *RHSValue);
return Env.makeAtomicBoolValue();
}
class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
public:
TransferVisitor(const StmtToEnvMap &StmtToEnv, Environment &Env)
@ -83,8 +104,16 @@ public:
Env.setValue(Loc, Env.makeOr(LHSVal, RHSVal));
break;
}
case BO_NE:
case BO_EQ: {
auto &LHSEqRHSValue = evaluateBooleanEquality(*LHS, *RHS, Env);
auto &Loc = Env.createStorageLocation(*S);
Env.setStorageLocation(*S, Loc);
Env.setValue(Loc, S->getOpcode() == BO_EQ ? LHSEqRHSValue
: Env.makeNot(LHSEqRHSValue));
break;
}
default:
// FIXME: Add support for BO_EQ, BO_NE.
break;
}
}

View File

@ -2578,4 +2578,74 @@ TEST_F(TransferTest, AssignMemberBeforeCopy) {
});
}
TEST_F(TransferTest, BooleanEquality) {
std::string Code = R"(
void target(bool Bar) {
bool Foo = true;
if (Bar == Foo) {
(void)0;
/*[[p-then]]*/
} else {
(void)0;
/*[[p-else]]*/
}
}
)";
runDataflow(
Code, [](llvm::ArrayRef<
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
Results,
ASTContext &ASTCtx) {
ASSERT_THAT(Results, ElementsAre(Pair("p-else", _), Pair("p-then", _)));
const Environment &EnvElse = Results[0].second.Env;
const Environment &EnvThen = Results[1].second.Env;
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
ASSERT_THAT(BarDecl, NotNull());
auto &BarValThen =
*cast<BoolValue>(EnvThen.getValue(*BarDecl, SkipPast::None));
EXPECT_TRUE(EnvThen.flowConditionImplies(BarValThen));
auto &BarValElse =
*cast<BoolValue>(EnvElse.getValue(*BarDecl, SkipPast::None));
EXPECT_FALSE(EnvElse.flowConditionImplies(BarValElse));
});
}
TEST_F(TransferTest, BooleanInequality) {
std::string Code = R"(
void target(bool Bar) {
bool Foo = true;
if (Bar != Foo) {
(void)0;
/*[[p-then]]*/
} else {
(void)0;
/*[[p-else]]*/
}
}
)";
runDataflow(
Code, [](llvm::ArrayRef<
std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
Results,
ASTContext &ASTCtx) {
ASSERT_THAT(Results, ElementsAre(Pair("p-else", _), Pair("p-then", _)));
const Environment &EnvElse = Results[0].second.Env;
const Environment &EnvThen = Results[1].second.Env;
const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
ASSERT_THAT(BarDecl, NotNull());
auto &BarValThen =
*cast<BoolValue>(EnvThen.getValue(*BarDecl, SkipPast::None));
EXPECT_FALSE(EnvThen.flowConditionImplies(BarValThen));
auto &BarValElse =
*cast<BoolValue>(EnvElse.getValue(*BarDecl, SkipPast::None));
EXPECT_TRUE(EnvElse.flowConditionImplies(BarValElse));
});
}
} // namespace