[clang] c8f822a - [clang][dataflow] Ensure well-formed flow conditions.
Yitzhak Mandelbaum via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 20 10:02:05 PDT 2022
Author: Yitzhak Mandelbaum
Date: 2022-04-20T17:01:55Z
New Revision: c8f822ad51951094504866049546bd2c3446728f
URL: https://github.com/llvm/llvm-project/commit/c8f822ad51951094504866049546bd2c3446728f
DIFF: https://github.com/llvm/llvm-project/commit/c8f822ad51951094504866049546bd2c3446728f.diff
LOG: [clang][dataflow] Ensure well-formed flow conditions.
Ensure that the expressions associated with terminators are associated with a
value. Otherwise, we can generate degenerate flow conditions, where both
branches share the same condition.
Differential Revision: https://reviews.llvm.org/D123858
Added:
Modified:
clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
Removed:
################################################################################
diff --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
index 6baf1a7fd42d6..72e6405724608 100644
--- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -32,6 +32,7 @@
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
namespace clang {
namespace dataflow {
@@ -106,10 +107,24 @@ class TerminatorVisitor : public ConstStmtVisitor<TerminatorVisitor> {
if (Env.getValue(Cond, SkipPast::None) == nullptr)
transfer(StmtToEnv, Cond, Env);
+ // FIXME: The flow condition must be an r-value, so `SkipPast::None` should
+ // suffice.
auto *Val =
cast_or_null<BoolValue>(Env.getValue(Cond, SkipPast::Reference));
- if (Val == nullptr)
- return;
+ // Value merging depends on flow conditions from
diff erent environments
+ // being mutually exclusive -- that is, they cannot both be true in their
+ // entirety (even if they may share some clauses). So, we need *some* value
+ // for the condition expression, even if just an atom.
+ if (Val == nullptr) {
+ // FIXME: Consider introducing a helper for this get-or-create pattern.
+ auto *Loc = Env.getStorageLocation(Cond, SkipPast::None);
+ if (Loc == nullptr) {
+ Loc = &Env.createStorageLocation(Cond);
+ Env.setStorageLocation(Cond, *Loc);
+ }
+ Val = &Env.makeAtomicBoolValue();
+ Env.setValue(*Loc, *Val);
+ }
// The condition must be inverted for the successor that encompasses the
// "else" branch, if such exists.
diff --git a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
index 2f5185a47e3e2..0a469dbd73dd4 100644
--- a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -878,4 +878,119 @@ TEST_F(FlowConditionTest, Join) {
});
}
+// Verifies that flow conditions are properly constructed even when the
+// condition is not meaningfully interpreted.
+//
+// Note: currently, arbitrary function calls are uninterpreted, so the test
+// exercises this case. If and when we change that, this test will not add to
+// coverage (although it may still test a valuable case).
+TEST_F(FlowConditionTest, OpaqueFlowConditionMergesToOpaqueBool) {
+ std::string Code = R"(
+ bool foo();
+
+ void target() {
+ bool Bar = true;
+ if (foo())
+ Bar = false;
+ (void)0;
+ /*[[p]]*/
+ }
+ )";
+ runDataflow(
+ Code, [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ auto &BarVal =
+ *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::Reference));
+
+ EXPECT_FALSE(Env.flowConditionImplies(BarVal));
+ });
+}
+
+// Verifies that flow conditions are properly constructed even when the
+// condition is not meaningfully interpreted.
+//
+// Note: currently, fields with recursive type calls are uninterpreted (beneath
+// the first instance), so the test exercises this case. If and when we change
+// that, this test will not add to coverage (although it may still test a
+// valuable case).
+TEST_F(FlowConditionTest, OpaqueFieldFlowConditionMergesToOpaqueBool) {
+ std::string Code = R"(
+ struct Rec {
+ Rec* Next;
+ };
+
+ struct Foo {
+ Rec* X;
+ };
+
+ void target(Foo F) {
+ bool Bar = true;
+ if (F.X->Next)
+ Bar = false;
+ (void)0;
+ /*[[p]]*/
+ }
+ )";
+ runDataflow(
+ Code, [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ auto &BarVal =
+ *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::Reference));
+
+ EXPECT_FALSE(Env.flowConditionImplies(BarVal));
+ });
+}
+
+// Verifies that flow conditions are properly constructed even when the
+// condition is not meaningfully interpreted. Adds to above by nesting the
+// interestnig case inside a normal branch. This protects against degenerate
+// solutions which only test for empty flow conditions, for example.
+TEST_F(FlowConditionTest, OpaqueFlowConditionInsideBranchMergesToOpaqueBool) {
+ std::string Code = R"(
+ bool foo();
+
+ void target(bool Cond) {
+ bool Bar = true;
+ if (Cond) {
+ if (foo())
+ Bar = false;
+ (void)0;
+ /*[[p]]*/
+ }
+ }
+ )";
+ runDataflow(
+ Code, [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
+ const Environment &Env = Results[0].second.Env;
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ auto &BarVal =
+ *cast<BoolValue>(Env.getValue(*BarDecl, SkipPast::Reference));
+
+ EXPECT_FALSE(Env.flowConditionImplies(BarVal));
+ });
+}
+
} // namespace
More information about the cfe-commits
mailing list