[clang] 6b8800d - [clang][dataflow] Enable comparison of distinct values in Environment
Stanislav Gatev via cfe-commits
cfe-commits at lists.llvm.org
Tue Feb 1 07:27:26 PST 2022
Author: Stanislav Gatev
Date: 2022-02-01T15:25:59Z
New Revision: 6b8800dfb5c9486c7302cc2d8d2ae362ef8fdfbd
URL: https://github.com/llvm/llvm-project/commit/6b8800dfb5c9486c7302cc2d8d2ae362ef8fdfbd
DIFF: https://github.com/llvm/llvm-project/commit/6b8800dfb5c9486c7302cc2d8d2ae362ef8fdfbd.diff
LOG: [clang][dataflow] Enable comparison of distinct values in Environment
Make specializations of `DataflowAnalysis` extendable with domain-specific
logic for comparing distinct values when comparing environments.
This includes a breaking change to the `runDataflowAnalysis` interface
as the return type is now `llvm::Expected<...>`.
This is part of the implementation of the dataflow analysis framework.
See "[RFC] A dataflow analysis framework for Clang AST" on cfe-dev.
Reviewed-by: ymandel, xazax.hun
Differential Revision: https://reviews.llvm.org/D118596
Added:
Modified:
clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
clang/unittests/Analysis/FlowSensitive/TestingSupport.h
clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
index f327abe63751f..b5a7c061e17bb 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
@@ -27,6 +27,7 @@
#include "llvm/ADT/Any.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
namespace clang {
namespace dataflow {
@@ -106,18 +107,24 @@ template <typename LatticeT> struct DataflowAnalysisState {
/// Performs dataflow analysis and returns a mapping from basic block IDs to
/// dataflow analysis states that model the respective basic blocks. Indices
-/// of the returned vector correspond to basic block IDs.
+/// of the returned vector correspond to basic block IDs. Returns an error if
+/// the dataflow analysis cannot be performed successfully.
template <typename AnalysisT>
-std::vector<llvm::Optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>
+llvm::Expected<std::vector<
+ llvm::Optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>>
runDataflowAnalysis(const ControlFlowContext &CFCtx, AnalysisT &Analysis,
const Environment &InitEnv) {
auto TypeErasedBlockStates =
runTypeErasedDataflowAnalysis(CFCtx, Analysis, InitEnv);
+ if (!TypeErasedBlockStates)
+ return TypeErasedBlockStates.takeError();
+
std::vector<
llvm::Optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>
BlockStates;
- BlockStates.reserve(TypeErasedBlockStates.size());
- llvm::transform(std::move(TypeErasedBlockStates),
+ BlockStates.reserve(TypeErasedBlockStates->size());
+
+ llvm::transform(std::move(*TypeErasedBlockStates),
std::back_inserter(BlockStates), [](auto &OptState) {
return std::move(OptState).map([](auto &&State) {
return DataflowAnalysisState<typename AnalysisT::Lattice>{
diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
index e560305cf5ca2..cebfb66ef242f 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -51,19 +51,36 @@ enum class SkipPast {
/// Holds the state of the program (store and heap) at a given program point.
class Environment {
public:
- /// Supplements `Environment` with non-standard join operations.
- class Merger {
+ /// Supplements `Environment` with non-standard comparison and join
+ /// operations.
+ class ValueModel {
public:
- virtual ~Merger() = default;
+ virtual ~ValueModel() = default;
- /// Given distinct `Val1` and `Val2`, modifies `MergedVal` to approximate
- /// both `Val1` and `Val2`. This could be a strict lattice join or a more
- /// general widening operation. If this function returns true, `MergedVal`
- /// will be assigned to a storage location of type `Type` in `Env`.
+ /// Returns true if and only if `Val1` is equivalent to `Val2`.
///
/// Requirements:
///
/// `Val1` and `Val2` must be distinct.
+ ///
+ /// `Val1` and `Val2` must model values of type `Type`.
+ virtual bool compareEquivalent(QualType Type, const Value &Val1,
+ const Value &Val2) {
+ // FIXME: Consider adding QualType to StructValue and removing the Type
+ // argument here.
+ return false;
+ }
+
+ /// Modifies `MergedVal` to approximate both `Val1` and `Val2`. This could
+ /// be a strict lattice join or a more general widening operation. If this
+ /// function returns true, `MergedVal` will be assigned to a storage
+ /// location of type `Type` in `Env`.
+ ///
+ /// Requirements:
+ ///
+ /// `Val1` and `Val2` must be distinct.
+ ///
+ /// `Val1`, `Val2`, and `MergedVal` must model values of type `Type`.
virtual bool merge(QualType Type, const Value &Val1, const Value &Val2,
Value &MergedVal, Environment &Env) {
return false;
@@ -84,9 +101,29 @@ class Environment {
/// with a symbolic representation of the `this` pointee.
Environment(DataflowAnalysisContext &DACtx, const DeclContext &DeclCtx);
- bool operator==(const Environment &) const;
-
- LatticeJoinEffect join(const Environment &, Environment::Merger &);
+ /// Returns true if and only if the environment is equivalent to `Other`, i.e
+ /// the two environments:
+ /// - have the same mappings from declarations to storage locations,
+ /// - have the same mappings from expressions to storage locations,
+ /// - have the same or equivalent (according to `Model`) values assigned to
+ /// the same storage locations.
+ ///
+ /// Requirements:
+ ///
+ /// `Other` and `this` must use the same `DataflowAnalysisContext`.
+ bool equivalentTo(const Environment &Other,
+ Environment::ValueModel &Model) const;
+
+ /// Joins the environment with `Other` by taking the intersection of storage
+ /// locations and values that are stored in them. Distinct values that are
+ /// assigned to the same storage locations in the environment and `Other` are
+ /// merged using `Model`.
+ ///
+ /// Requirements:
+ ///
+ /// `Other` and `this` must use the same `DataflowAnalysisContext`.
+ LatticeJoinEffect join(const Environment &Other,
+ Environment::ValueModel &Model);
// FIXME: Rename `createOrGetStorageLocation` to `getOrCreateStorageLocation`,
// `getStableStorageLocation`, or something more appropriate.
diff --git a/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h b/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
index 9f44475b14ba1..2d3a9e456370d 100644
--- a/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
+++ b/clang/include/clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h
@@ -25,6 +25,7 @@
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
#include "llvm/ADT/Any.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/Support/Error.h"
namespace clang {
namespace dataflow {
@@ -40,7 +41,7 @@ struct TypeErasedLattice {
};
/// Type-erased base class for dataflow analyses built on a single lattice type.
-class TypeErasedDataflowAnalysis : public Environment::Merger {
+class TypeErasedDataflowAnalysis : public Environment::ValueModel {
/// Determines whether to apply the built-in transfer functions.
// FIXME: Remove this option once the framework supports composing analyses
// (at which point the built-in transfer functions can be simply a standalone
@@ -115,8 +116,9 @@ TypeErasedDataflowAnalysisState transferBlock(
/// Performs dataflow analysis and returns a mapping from basic block IDs to
/// dataflow analysis states that model the respective basic blocks. Indices
-/// of the returned vector correspond to basic block IDs.
-std::vector<llvm::Optional<TypeErasedDataflowAnalysisState>>
+/// of the returned vector correspond to basic block IDs. Returns an error if
+/// the dataflow analysis cannot be performed successfully.
+llvm::Expected<std::vector<llvm::Optional<TypeErasedDataflowAnalysisState>>>
runTypeErasedDataflowAnalysis(const ControlFlowContext &CFCtx,
TypeErasedDataflowAnalysis &Analysis,
const Environment &InitEnv);
diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index 8392f959d798e..eca58b313761b 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -41,6 +41,21 @@ llvm::DenseMap<K, V> intersectDenseMaps(const llvm::DenseMap<K, V> &Map1,
return Result;
}
+/// Returns true if and only if `Val1` is equivalent to `Val2`.
+static bool equivalentValues(QualType Type, Value *Val1, Value *Val2,
+ Environment::ValueModel &Model) {
+ if (Val1 == Val2)
+ return true;
+
+ if (auto *IndVal1 = dyn_cast<IndirectionValue>(Val1)) {
+ auto *IndVal2 = cast<IndirectionValue>(Val2);
+ assert(IndVal1->getKind() == IndVal2->getKind());
+ return &IndVal1->getPointeeLoc() == &IndVal2->getPointeeLoc();
+ }
+
+ return Model.compareEquivalent(Type, *Val1, *Val2);
+}
+
Environment::Environment(DataflowAnalysisContext &DACtx,
const DeclContext &DeclCtx)
: Environment(DACtx) {
@@ -68,13 +83,40 @@ Environment::Environment(DataflowAnalysisContext &DACtx,
}
}
-bool Environment::operator==(const Environment &Other) const {
+bool Environment::equivalentTo(const Environment &Other,
+ Environment::ValueModel &Model) const {
assert(DACtx == Other.DACtx);
- return DeclToLoc == Other.DeclToLoc && LocToVal == Other.LocToVal;
+
+ if (DeclToLoc != Other.DeclToLoc)
+ return false;
+
+ if (ExprToLoc != Other.ExprToLoc)
+ return false;
+
+ if (LocToVal.size() != Other.LocToVal.size())
+ return false;
+
+ for (auto &Entry : LocToVal) {
+ const StorageLocation *Loc = Entry.first;
+ assert(Loc != nullptr);
+
+ Value *Val = Entry.second;
+ assert(Val != nullptr);
+
+ auto It = Other.LocToVal.find(Loc);
+ if (It == Other.LocToVal.end())
+ return false;
+ assert(It->second != nullptr);
+
+ if (!equivalentValues(Loc->getType(), Val, It->second, Model))
+ return false;
+ }
+
+ return true;
}
LatticeJoinEffect Environment::join(const Environment &Other,
- Environment::Merger &Merger) {
+ Environment::ValueModel &Model) {
assert(DACtx == Other.DACtx);
auto Effect = LatticeJoinEffect::Unchanged;
@@ -89,7 +131,7 @@ LatticeJoinEffect Environment::join(const Environment &Other,
if (ExprToLocSizeBefore != ExprToLoc.size())
Effect = LatticeJoinEffect::Changed;
- // Move `LocToVal` so that `Environment::Merger::merge` can safely assign
+ // Move `LocToVal` so that `Environment::ValueModel::merge` can safely assign
// values to storage locations while this code iterates over the current
// assignments.
llvm::DenseMap<const StorageLocation *, Value *> OldLocToVal =
@@ -106,23 +148,16 @@ LatticeJoinEffect Environment::join(const Environment &Other,
continue;
assert(It->second != nullptr);
- if (It->second == Val) {
+ if (equivalentValues(Loc->getType(), Val, It->second, Model)) {
LocToVal.insert({Loc, Val});
continue;
}
- if (auto *FirstVal = dyn_cast<PointerValue>(Val)) {
- auto *SecondVal = cast<PointerValue>(It->second);
- if (&FirstVal->getPointeeLoc() == &SecondVal->getPointeeLoc()) {
- LocToVal.insert({Loc, FirstVal});
- continue;
- }
- }
-
- // FIXME: Consider destroying `MergedValue` immediately if `Merger::merge`
- // returns false to avoid storing unneeded values in `DACtx`.
+ // FIXME: Consider destroying `MergedValue` immediately if
+ // `ValueModel::merge` returns false to avoid storing unneeded values in
+ // `DACtx`.
if (Value *MergedVal = createValue(Loc->getType()))
- if (Merger.merge(Loc->getType(), *Val, *It->second, *MergedVal, *this))
+ if (Model.merge(Loc->getType(), *Val, *It->second, *MergedVal, *this))
LocToVal.insert({Loc, MergedVal});
}
if (OldLocToVal.size() != LocToVal.size())
diff --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
index aaf6a834f5b3a..6b14b5ceaf69a 100644
--- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include <memory>
+#include <system_error>
#include <utility>
#include <vector>
@@ -26,7 +27,7 @@
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/Optional.h"
-#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/Error.h"
namespace clang {
namespace dataflow {
@@ -190,7 +191,7 @@ TypeErasedDataflowAnalysisState transferBlock(
return State;
}
-std::vector<llvm::Optional<TypeErasedDataflowAnalysisState>>
+llvm::Expected<std::vector<llvm::Optional<TypeErasedDataflowAnalysisState>>>
runTypeErasedDataflowAnalysis(const ControlFlowContext &CFCtx,
TypeErasedDataflowAnalysis &Analysis,
const Environment &InitEnv) {
@@ -216,8 +217,8 @@ runTypeErasedDataflowAnalysis(const ControlFlowContext &CFCtx,
static constexpr uint32_t MaxIterations = 1 << 16;
while (const CFGBlock *Block = Worklist.dequeue()) {
if (++Iterations > MaxIterations) {
- llvm::errs() << "Maximum number of iterations reached, giving up.\n";
- break;
+ return llvm::createStringError(std::errc::timed_out,
+ "maximum number of iterations reached");
}
const llvm::Optional<TypeErasedDataflowAnalysisState> &OldBlockState =
@@ -228,7 +229,7 @@ runTypeErasedDataflowAnalysis(const ControlFlowContext &CFCtx,
if (OldBlockState.hasValue() &&
Analysis.isEqualTypeErased(OldBlockState.getValue().Lattice,
NewBlockState.Lattice) &&
- OldBlockState->Env == NewBlockState.Env) {
+ OldBlockState->Env.equivalentTo(NewBlockState.Env, Analysis)) {
// The state of `Block` didn't change after transfer so there's no need to
// revisit its successors.
continue;
diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h
index 276441359d05d..3815076294b18 100644
--- a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h
+++ b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h
@@ -112,11 +112,13 @@ llvm::Error checkDataflow(
StmtToAnnotations = buildStatementToAnnotationMapping(F, AnnotatedCode);
if (!StmtToAnnotations)
return StmtToAnnotations.takeError();
-
auto &Annotations = *StmtToAnnotations;
- std::vector<llvm::Optional<TypeErasedDataflowAnalysisState>> BlockStates =
- runTypeErasedDataflowAnalysis(*CFCtx, Analysis, Env);
+ llvm::Expected<std::vector<llvm::Optional<TypeErasedDataflowAnalysisState>>>
+ MaybeBlockStates = runTypeErasedDataflowAnalysis(*CFCtx, Analysis, Env);
+ if (!MaybeBlockStates)
+ return MaybeBlockStates.takeError();
+ auto &BlockStates = *MaybeBlockStates;
if (BlockStates.empty()) {
Expectations({}, Context);
diff --git a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
index e9d5e129e32d6..90d7d73c85a55 100644
--- a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp
@@ -19,6 +19,7 @@
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
#include "clang/Analysis/FlowSensitive/Value.h"
#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringRef.h"
@@ -49,53 +50,35 @@ using ::testing::Test;
using ::testing::UnorderedElementsAre;
template <typename AnalysisT>
-class AnalysisCallback : public ast_matchers::MatchFinder::MatchCallback {
-public:
- AnalysisCallback(AnalysisT (*MakeAnalysis)(ASTContext &))
- : MakeAnalysis(MakeAnalysis) {}
- void run(const ast_matchers::MatchFinder::MatchResult &Result) override {
- assert(BlockStates.empty());
-
- const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("func");
- assert(Func != nullptr);
-
- Stmt *Body = Func->getBody();
- assert(Body != nullptr);
-
- auto CFCtx = llvm::cantFail(
- ControlFlowContext::build(nullptr, Body, Result.Context));
-
- AnalysisT Analysis = MakeAnalysis(*Result.Context);
- DataflowAnalysisContext DACtx;
- Environment Env(DACtx);
- BlockStates = runDataflowAnalysis(CFCtx, Analysis, Env);
- }
-
- AnalysisT (*MakeAnalysis)(ASTContext &);
- std::vector<
- llvm::Optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>
- BlockStates;
-};
-
-template <typename AnalysisT>
-std::vector<llvm::Optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>
+llvm::Expected<std::vector<
+ llvm::Optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>>
runAnalysis(llvm::StringRef Code, AnalysisT (*MakeAnalysis)(ASTContext &)) {
std::unique_ptr<ASTUnit> AST =
tooling::buildASTFromCodeWithArgs(Code, {"-std=c++11"});
- AnalysisCallback<AnalysisT> Callback(MakeAnalysis);
- ast_matchers::MatchFinder Finder;
- Finder.addMatcher(
- ast_matchers::functionDecl(ast_matchers::hasName("target")).bind("func"),
- &Callback);
- Finder.matchAST(AST->getASTContext());
+ auto *Func = selectFirst<FunctionDecl>(
+ "func", match(functionDecl(ast_matchers::hasName("target")).bind("func"),
+ AST->getASTContext()));
+ assert(Func != nullptr);
+
+ Stmt *Body = Func->getBody();
+ assert(Body != nullptr);
- return Callback.BlockStates;
+ auto CFCtx = llvm::cantFail(
+ ControlFlowContext::build(nullptr, Body, &AST->getASTContext()));
+
+ AnalysisT Analysis = MakeAnalysis(AST->getASTContext());
+ DataflowAnalysisContext DACtx;
+ Environment Env(DACtx);
+
+ return runDataflowAnalysis(CFCtx, Analysis, Env);
}
TEST(DataflowAnalysisTest, NoopAnalysis) {
- auto BlockStates = runAnalysis<NoopAnalysis>(
- "void target() {}", [](ASTContext &C) { return NoopAnalysis(C, false); });
+ auto BlockStates = llvm::cantFail(
+ runAnalysis<NoopAnalysis>("void target() {}", [](ASTContext &C) {
+ return NoopAnalysis(C, false);
+ }));
EXPECT_EQ(BlockStates.size(), 2u);
EXPECT_TRUE(BlockStates[0].hasValue());
EXPECT_TRUE(BlockStates[1].hasValue());
@@ -132,18 +115,15 @@ class NonConvergingAnalysis
};
TEST(DataflowAnalysisTest, NonConvergingAnalysis) {
- auto BlockStates = runAnalysis<NonConvergingAnalysis>(
- R"(
+ std::string Code = R"(
void target() {
while(true) {}
}
- )",
- [](ASTContext &C) { return NonConvergingAnalysis(C); });
- EXPECT_EQ(BlockStates.size(), 4u);
- EXPECT_TRUE(BlockStates[0].hasValue());
- EXPECT_TRUE(BlockStates[1].hasValue());
- EXPECT_TRUE(BlockStates[2].hasValue());
- EXPECT_TRUE(BlockStates[3].hasValue());
+ )";
+ auto Res = runAnalysis<NonConvergingAnalysis>(
+ Code, [](ASTContext &C) { return NonConvergingAnalysis(C); });
+ EXPECT_EQ(llvm::toString(Res.takeError()),
+ "maximum number of iterations reached");
}
struct FunctionCallLattice {
@@ -317,8 +297,9 @@ TEST_F(NoreturnDestructorTest, ConditionalOperatorNestedBranchReturns) {
class OptionalIntAnalysis
: public DataflowAnalysis<OptionalIntAnalysis, NoopLattice> {
public:
- explicit OptionalIntAnalysis(ASTContext &Context)
- : DataflowAnalysis<OptionalIntAnalysis, NoopLattice>(Context) {}
+ explicit OptionalIntAnalysis(ASTContext &Context, BoolValue &HasValueTop)
+ : DataflowAnalysis<OptionalIntAnalysis, NoopLattice>(Context),
+ HasValueTop(HasValueTop) {}
static NoopLattice initialElement() { return {}; }
@@ -353,8 +334,20 @@ class OptionalIntAnalysis
}
}
+ bool compareEquivalent(QualType Type, const Value &Val1,
+ const Value &Val2) final {
+ // Nothing to say about a value that does not model an `OptionalInt`.
+ if (!Type->isRecordType() ||
+ Type->getAsCXXRecordDecl()->getQualifiedNameAsString() != "OptionalInt")
+ return false;
+
+ return cast<StructValue>(&Val1)->getProperty("has_value") ==
+ cast<StructValue>(&Val2)->getProperty("has_value");
+ }
+
bool merge(QualType Type, const Value &Val1, const Value &Val2,
Value &MergedVal, Environment &Env) final {
+ // Nothing to say about a value that does not model an `OptionalInt`.
if (!Type->isRecordType() ||
Type->getAsCXXRecordDecl()->getQualifiedNameAsString() != "OptionalInt")
return false;
@@ -369,12 +362,12 @@ class OptionalIntAnalysis
if (HasValue2 == nullptr)
return false;
- if (HasValue1 != HasValue2)
- return false;
-
- cast<StructValue>(&MergedVal)->setProperty("has_value", *HasValue1);
+ assert(HasValue1 != HasValue2);
+ cast<StructValue>(&MergedVal)->setProperty("has_value", HasValueTop);
return true;
}
+
+ BoolValue &HasValueTop;
};
class WideningTest : public Test {
@@ -392,8 +385,10 @@ class WideningTest : public Test {
ASSERT_THAT_ERROR(
test::checkDataflow<OptionalIntAnalysis>(
Code, "target",
- [](ASTContext &Context, Environment &Env) {
- return OptionalIntAnalysis(Context);
+ [this](ASTContext &Context, Environment &Env) {
+ assert(HasValueTop == nullptr);
+ HasValueTop = &Env.takeOwnership(std::make_unique<BoolValue>());
+ return OptionalIntAnalysis(Context, *HasValueTop);
},
[&Match](
llvm::ArrayRef<
@@ -403,6 +398,8 @@ class WideningTest : public Test {
{"-fsyntax-only", "-std=c++17"}, FilesContents),
llvm::Succeeded());
}
+
+ BoolValue *HasValueTop = nullptr;
};
TEST_F(WideningTest, JoinDistinctValuesWithDistinctProperties) {
@@ -421,10 +418,11 @@ TEST_F(WideningTest, JoinDistinctValuesWithDistinctProperties) {
}
)";
runDataflow(
- Code, [](llvm::ArrayRef<
- std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
- Results,
- ASTContext &ASTCtx) {
+ Code,
+ [this](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
ASSERT_THAT(Results,
ElementsAre(Pair("p3", _), Pair("p2", _), Pair("p1", _)));
const Environment &Env1 = Results[2].second.Env;
@@ -442,7 +440,7 @@ TEST_F(WideningTest, JoinDistinctValuesWithDistinctProperties) {
&Env1.getBoolLiteralValue(false));
EXPECT_EQ(GetFooValue(Env2)->getProperty("has_value"),
&Env2.getBoolLiteralValue(true));
- EXPECT_THAT(Env3.getValue(*FooDecl, SkipPast::None), IsNull());
+ EXPECT_EQ(GetFooValue(Env3)->getProperty("has_value"), HasValueTop);
});
}
@@ -494,7 +492,7 @@ TEST_F(WideningTest, JoinDistinctValuesWithSameProperties) {
});
}
-TEST_F(WideningTest, DistinctPointersToTheSameLocation) {
+TEST_F(WideningTest, DistinctPointersToTheSameLocationAreEquivalent) {
std::string Code = R"(
void target(int Foo, bool Cond) {
int *Bar = &Foo;
@@ -527,4 +525,36 @@ TEST_F(WideningTest, DistinctPointersToTheSameLocation) {
});
}
+TEST_F(WideningTest, DistinctValuesWithSamePropertiesAreEquivalent) {
+ std::string Code = R"(
+ #include "widening_test_defs.h"
+
+ void target(bool Cond) {
+ OptionalInt Foo;
+ Foo = 1;
+ while (Cond) {
+ Foo = 2;
+ }
+ (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 *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const auto *FooVal =
+ cast<StructValue>(Env.getValue(*FooDecl, SkipPast::None));
+ EXPECT_EQ(FooVal->getProperty("has_value"),
+ &Env.getBoolLiteralValue(true));
+ });
+}
+
} // namespace
More information about the cfe-commits
mailing list