sgatev created this revision.
sgatev added reviewers: ymandel, xazax.hun, gribozavr2.
Herald added a subscriber: rnkovacs.
sgatev requested review of this revision.
Herald added a project: clang.
This is part of the implementation of the dataflow analysis framework.
See "[RFC] A dataflow analysis framework for Clang AST" on cfe-dev.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D116596
Files:
clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
clang/lib/Analysis/FlowSensitive/Transfer.cpp
clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Index: clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
===================================================================
--- clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -82,10 +82,11 @@
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
ASSERT_THAT(FooDecl, NotNull());
- const StorageLocation *FooLoc = Env.getStorageLocation(*FooDecl);
+ const StorageLocation *FooLoc =
+ Env.getStorageLocation(*FooDecl, SkipPast::None);
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
- const Value *FooVal = Env.getValue(*FooLoc);
+ const Value *FooVal = Env.getValue(*FooLoc, SkipPast::None);
ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
});
}
@@ -125,14 +126,15 @@
}
ASSERT_THAT(BarDecl, NotNull());
- const auto *FooLoc =
- cast<AggregateStorageLocation>(Env.getStorageLocation(*FooDecl));
+ const auto *FooLoc = cast<AggregateStorageLocation>(
+ Env.getStorageLocation(*FooDecl, SkipPast::None));
const auto *BarLoc =
cast<ScalarStorageLocation>(&FooLoc->getChild(*BarDecl));
- const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
+ const auto *FooVal =
+ cast<StructValue>(Env.getValue(*FooLoc, SkipPast::None));
const auto *BarVal = cast<IntegerValue>(&FooVal->getChild(*BarDecl));
- ASSERT_EQ(Env.getValue(*BarLoc), BarVal);
+ ASSERT_EQ(Env.getValue(*BarLoc, SkipPast::None), BarVal);
});
}
@@ -171,14 +173,15 @@
}
ASSERT_THAT(BarDecl, NotNull());
- const auto *FooLoc =
- cast<AggregateStorageLocation>(Env.getStorageLocation(*FooDecl));
+ const auto *FooLoc = cast<AggregateStorageLocation>(
+ Env.getStorageLocation(*FooDecl, SkipPast::None));
const auto *BarLoc =
cast<ScalarStorageLocation>(&FooLoc->getChild(*BarDecl));
- const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
+ const auto *FooVal =
+ cast<StructValue>(Env.getValue(*FooLoc, SkipPast::None));
const auto *BarVal = cast<IntegerValue>(&FooVal->getChild(*BarDecl));
- ASSERT_EQ(Env.getValue(*BarLoc), BarVal);
+ ASSERT_EQ(Env.getValue(*BarLoc, SkipPast::None), BarVal);
});
}
@@ -204,15 +207,17 @@
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
ASSERT_THAT(FooDecl, NotNull());
- const StorageLocation *FooLoc = Env.getStorageLocation(*FooDecl);
+ const StorageLocation *FooLoc =
+ Env.getStorageLocation(*FooDecl, SkipPast::None);
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
const ReferenceValue *FooVal =
- cast<ReferenceValue>(Env.getValue(*FooLoc));
+ cast<ReferenceValue>(Env.getValue(*FooLoc, SkipPast::None));
const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc();
ASSERT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
- const Value *FooPointeeVal = Env.getValue(FooPointeeLoc);
+ const Value *FooPointeeVal =
+ Env.getValue(FooPointeeLoc, SkipPast::None);
ASSERT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal));
});
}
@@ -293,36 +298,37 @@
ASSERT_THAT(BazRefDecl, NotNull());
ASSERT_THAT(BazPtrDecl, NotNull());
- const auto *FooLoc =
- cast<ScalarStorageLocation>(Env.getStorageLocation(*FooDecl));
- const auto *FooVal = cast<ReferenceValue>(Env.getValue(*FooLoc));
- const auto *FooPointeeVal =
- cast<StructValue>(Env.getValue(FooVal->getPointeeLoc()));
+ const auto *FooLoc = cast<ScalarStorageLocation>(
+ Env.getStorageLocation(*FooDecl, SkipPast::None));
+ const auto *FooVal =
+ cast<ReferenceValue>(Env.getValue(*FooLoc, SkipPast::None));
+ const auto *FooPointeeVal = cast<StructValue>(
+ Env.getValue(FooVal->getPointeeLoc(), SkipPast::None));
const auto *BarVal =
cast<ReferenceValue>(&FooPointeeVal->getChild(*BarDecl));
- const auto *BarPointeeVal =
- cast<StructValue>(Env.getValue(BarVal->getPointeeLoc()));
+ const auto *BarPointeeVal = cast<StructValue>(
+ Env.getValue(BarVal->getPointeeLoc(), SkipPast::None));
const auto *FooRefVal =
cast<ReferenceValue>(&BarPointeeVal->getChild(*FooRefDecl));
const StorageLocation &FooRefPointeeLoc = FooRefVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(FooRefPointeeLoc), IsNull());
+ ASSERT_THAT(Env.getValue(FooRefPointeeLoc, SkipPast::None), IsNull());
const auto *FooPtrVal =
cast<PointerValue>(&BarPointeeVal->getChild(*FooPtrDecl));
const StorageLocation &FooPtrPointeeLoc = FooPtrVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull());
+ ASSERT_THAT(Env.getValue(FooPtrPointeeLoc, SkipPast::None), IsNull());
const auto *BazRefVal =
cast<ReferenceValue>(&BarPointeeVal->getChild(*BazRefDecl));
const StorageLocation &BazRefPointeeLoc = BazRefVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(BazRefPointeeLoc), NotNull());
+ ASSERT_THAT(Env.getValue(BazRefPointeeLoc, SkipPast::None), NotNull());
const auto *BazPtrVal =
cast<PointerValue>(&BarPointeeVal->getChild(*BazPtrDecl));
const StorageLocation &BazPtrPointeeLoc = BazPtrVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull());
+ ASSERT_THAT(Env.getValue(BazPtrPointeeLoc, SkipPast::None), NotNull());
});
}
@@ -348,14 +354,17 @@
const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
ASSERT_THAT(FooDecl, NotNull());
- const StorageLocation *FooLoc = Env.getStorageLocation(*FooDecl);
+ const StorageLocation *FooLoc =
+ Env.getStorageLocation(*FooDecl, SkipPast::None);
ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
- const PointerValue *FooVal = cast<PointerValue>(Env.getValue(*FooLoc));
+ const PointerValue *FooVal =
+ cast<PointerValue>(Env.getValue(*FooLoc, SkipPast::None));
const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc();
ASSERT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
- const Value *FooPointeeVal = Env.getValue(FooPointeeLoc);
+ const Value *FooPointeeVal =
+ Env.getValue(FooPointeeLoc, SkipPast::None);
ASSERT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal));
});
}
@@ -449,36 +458,37 @@
ASSERT_THAT(BazRefDecl, NotNull());
ASSERT_THAT(BazPtrDecl, NotNull());
- const auto *FooLoc =
- cast<ScalarStorageLocation>(Env.getStorageLocation(*FooDecl));
- const auto *FooVal = cast<PointerValue>(Env.getValue(*FooLoc));
- const auto *FooPointeeVal =
- cast<StructValue>(Env.getValue(FooVal->getPointeeLoc()));
+ const auto *FooLoc = cast<ScalarStorageLocation>(
+ Env.getStorageLocation(*FooDecl, SkipPast::None));
+ const auto *FooVal =
+ cast<PointerValue>(Env.getValue(*FooLoc, SkipPast::None));
+ const auto *FooPointeeVal = cast<StructValue>(
+ Env.getValue(FooVal->getPointeeLoc(), SkipPast::None));
const auto *BarVal =
cast<PointerValue>(&FooPointeeVal->getChild(*BarDecl));
- const auto *BarPointeeVal =
- cast<StructValue>(Env.getValue(BarVal->getPointeeLoc()));
+ const auto *BarPointeeVal = cast<StructValue>(
+ Env.getValue(BarVal->getPointeeLoc(), SkipPast::None));
const auto *FooRefVal =
cast<ReferenceValue>(&BarPointeeVal->getChild(*FooRefDecl));
const StorageLocation &FooRefPointeeLoc = FooRefVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(FooRefPointeeLoc), IsNull());
+ ASSERT_THAT(Env.getValue(FooRefPointeeLoc, SkipPast::None), IsNull());
const auto *FooPtrVal =
cast<PointerValue>(&BarPointeeVal->getChild(*FooPtrDecl));
const StorageLocation &FooPtrPointeeLoc = FooPtrVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull());
+ ASSERT_THAT(Env.getValue(FooPtrPointeeLoc, SkipPast::None), IsNull());
const auto *BazRefVal =
cast<ReferenceValue>(&BarPointeeVal->getChild(*BazRefDecl));
const StorageLocation &BazRefPointeeLoc = BazRefVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(BazRefPointeeLoc), NotNull());
+ ASSERT_THAT(Env.getValue(BazRefPointeeLoc, SkipPast::None), NotNull());
const auto *BazPtrVal =
cast<PointerValue>(&BarPointeeVal->getChild(*BazPtrDecl));
const StorageLocation &BazPtrPointeeLoc = BazPtrVal->getPointeeLoc();
- ASSERT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull());
+ ASSERT_THAT(Env.getValue(BazPtrPointeeLoc, SkipPast::None), NotNull());
});
}
@@ -498,43 +508,148 @@
// [[p4]]
}
)";
- runDataflow(
- Code, [](llvm::ArrayRef<
- std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
- Results,
- ASTContext &ASTCtx) {
- ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
- Pair("p2", _), Pair("p1", _)));
- const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
- ASSERT_THAT(FooDecl, NotNull());
+ runDataflow(Code, [](llvm::ArrayRef<std::pair<
+ std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
+ Pair("p2", _), Pair("p1", _)));
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
+ ASSERT_THAT(FooDecl, NotNull());
- const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
- ASSERT_THAT(BarDecl, NotNull());
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
+ ASSERT_THAT(BarDecl, NotNull());
- const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz");
- ASSERT_THAT(BazDecl, NotNull());
-
- const Environment &Env1 = Results[3].second.Env;
- const StorageLocation *FooLoc = Env1.getStorageLocation(*FooDecl);
- ASSERT_THAT(FooLoc, NotNull());
- ASSERT_THAT(Env1.getStorageLocation(*BarDecl), IsNull());
- ASSERT_THAT(Env1.getStorageLocation(*BazDecl), IsNull());
-
- const Environment &Env2 = Results[2].second.Env;
- ASSERT_EQ(Env2.getStorageLocation(*FooDecl), FooLoc);
- ASSERT_THAT(Env2.getStorageLocation(*BarDecl), NotNull());
- ASSERT_THAT(Env2.getStorageLocation(*BazDecl), IsNull());
-
- const Environment &Env3 = Results[1].second.Env;
- ASSERT_EQ(Env3.getStorageLocation(*FooDecl), FooLoc);
- ASSERT_THAT(Env3.getStorageLocation(*BarDecl), IsNull());
- ASSERT_THAT(Env3.getStorageLocation(*BazDecl), NotNull());
-
- const Environment &Env4 = Results[0].second.Env;
- ASSERT_EQ(Env4.getStorageLocation(*FooDecl), FooLoc);
- ASSERT_THAT(Env4.getStorageLocation(*BarDecl), IsNull());
- ASSERT_THAT(Env4.getStorageLocation(*BazDecl), IsNull());
- });
+ const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz");
+ ASSERT_THAT(BazDecl, NotNull());
+
+ const Environment &Env1 = Results[3].second.Env;
+ const StorageLocation *FooLoc =
+ Env1.getStorageLocation(*FooDecl, SkipPast::None);
+ ASSERT_THAT(FooLoc, NotNull());
+ ASSERT_THAT(Env1.getStorageLocation(*BarDecl, SkipPast::None), IsNull());
+ ASSERT_THAT(Env1.getStorageLocation(*BazDecl, SkipPast::None), IsNull());
+
+ const Environment &Env2 = Results[2].second.Env;
+ ASSERT_EQ(Env2.getStorageLocation(*FooDecl, SkipPast::None), FooLoc);
+ ASSERT_THAT(Env2.getStorageLocation(*BarDecl, SkipPast::None), NotNull());
+ ASSERT_THAT(Env2.getStorageLocation(*BazDecl, SkipPast::None), IsNull());
+
+ const Environment &Env3 = Results[1].second.Env;
+ ASSERT_EQ(Env3.getStorageLocation(*FooDecl, SkipPast::None), FooLoc);
+ ASSERT_THAT(Env3.getStorageLocation(*BarDecl, SkipPast::None), IsNull());
+ ASSERT_THAT(Env3.getStorageLocation(*BazDecl, SkipPast::None), NotNull());
+
+ const Environment &Env4 = Results[0].second.Env;
+ ASSERT_EQ(Env4.getStorageLocation(*FooDecl, SkipPast::None), FooLoc);
+ ASSERT_THAT(Env4.getStorageLocation(*BarDecl, SkipPast::None), IsNull());
+ ASSERT_THAT(Env4.getStorageLocation(*BazDecl, SkipPast::None), IsNull());
+ });
+}
+
+TEST_F(TransferTest, BinaryOperatorAssign) {
+ std::string Code = R"(
+ void target() {
+ int foo;
+ int bar;
+ bar = foo;
+ // [[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 Value *FooVal = Env.getValue(*FooDecl, SkipPast::None);
+ ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const Value *BarVal = Env.getValue(*BarDecl, SkipPast::None);
+ ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
+
+ EXPECT_EQ(BarVal, FooVal);
+ });
+}
+
+TEST_F(TransferTest, VarDeclInitAssign) {
+ std::string Code = R"(
+ void target() {
+ int foo;
+ int bar = foo;
+ // [[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 Value *FooVal = Env.getValue(*FooDecl, SkipPast::None);
+ ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const Value *BarVal = Env.getValue(*BarDecl, SkipPast::None);
+ ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
+
+ EXPECT_EQ(BarVal, FooVal);
+ });
+}
+
+TEST_F(TransferTest, AssignToReference) {
+ std::string Code = R"(
+ void target() {
+ int foo;
+ int bar;
+ int& baz = foo;
+ // [[p1]]
+ baz = bar;
+ // [[p2]]
+ }
+ )";
+ runDataflow(Code,
+ [](llvm::ArrayRef<
+ std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
+ Results,
+ ASTContext &ASTCtx) {
+ ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _)));
+ const Environment &Env1 = Results[0].second.Env;
+ const Environment &Env2 = Results[1].second.Env;
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const Value *FooVal = Env1.getValue(*FooDecl, SkipPast::None);
+ ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
+
+ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
+ ASSERT_THAT(BarDecl, NotNull());
+
+ const Value *BarVal = Env1.getValue(*BarDecl, SkipPast::None);
+ ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
+
+ const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz");
+ ASSERT_THAT(BazDecl, NotNull());
+
+ EXPECT_EQ(Env1.getValue(*BazDecl, SkipPast::Reference), FooVal);
+ EXPECT_EQ(Env2.getValue(*BazDecl, SkipPast::Reference), BarVal);
+ });
}
} // namespace
Index: clang/lib/Analysis/FlowSensitive/Transfer.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -15,11 +15,14 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/OperationKinds.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
+#include "clang/Basic/OperatorKinds.h"
#include "llvm/Support/Casting.h"
#include <cassert>
+#include <memory>
namespace clang {
namespace dataflow {
@@ -28,6 +31,39 @@
public:
TransferVisitor(Environment &Env) : Env(Env) {}
+ void VisitBinaryOperator(const BinaryOperator *S) {
+ if (S->getOpcode() == BO_Assign) {
+ assert(S->getLHS() != nullptr);
+ auto *LHSLoc = Env.getStorageLocation(*S->getLHS(), SkipPast::Reference);
+ if (LHSLoc == nullptr)
+ return;
+
+ assert(S->getRHS() != nullptr);
+ Value *RHSVal = Env.getValue(*S->getRHS(), SkipPast::Reference);
+ if (RHSVal == nullptr)
+ return;
+
+ Env.setValue(*LHSLoc, *RHSVal);
+ }
+ // FIXME: Add support for BO_EQ, BO_NE.
+ }
+
+ void VisitDeclRefExpr(const DeclRefExpr *S) {
+ assert(S->getDecl() != nullptr);
+ auto *DeclLoc = Env.getStorageLocation(*S->getDecl(), SkipPast::None);
+ if (DeclLoc == nullptr)
+ return;
+
+ if (S->getDecl()->getType()->isReferenceType()) {
+ Env.setStorageLocation(*S, *DeclLoc);
+ } else {
+ auto &Loc = Env.createStorageLocation(*S);
+ auto &Val = Env.takeOwnership(std::make_unique<ReferenceValue>(*DeclLoc));
+ Env.setStorageLocation(*S, Loc);
+ Env.setValue(Loc, Val);
+ }
+ }
+
void VisitDeclStmt(const DeclStmt *S) {
// FIXME: Add support for group decls, e.g: `int a, b;`
if (S->isSingleDecl()) {
@@ -37,8 +73,22 @@
}
}
+ void VisitImplicitCastExpr(const ImplicitCastExpr *S) {
+ if (S->getCastKind() == CK_LValueToRValue) {
+ assert(S->getSubExpr() != nullptr);
+ auto *SubExprVal = Env.getValue(*S->getSubExpr(), SkipPast::Reference);
+ if (SubExprVal == nullptr)
+ return;
+
+ auto &ExprLoc = Env.createStorageLocation(*S);
+ Env.setStorageLocation(*S, ExprLoc);
+ Env.setValue(ExprLoc, *SubExprVal);
+ }
+ // FIXME: Add support for CK_NoOp, CK_UserDefinedConversion,
+ // CK_ConstructorConversion, CK_UncheckedDerivedToBase.
+ }
+
// FIXME: Add support for:
- // - BinaryOperator
// - CallExpr
// - CXXBindTemporaryExpr
// - CXXBoolLiteralExpr
@@ -47,8 +97,6 @@
// - CXXOperatorCallExpr
// - CXXStaticCastExpr
// - CXXThisExpr
- // - DeclRefExpr
- // - ImplicitCastExpr
// - MaterializeTemporaryExpr
// - MemberExpr
// - UnaryOperator
@@ -57,6 +105,24 @@
void visitVarDecl(const VarDecl &D) {
auto &Loc = Env.createStorageLocation(D);
Env.setStorageLocation(D, Loc);
+
+ if (const Expr *InitExpr = D.getInit()) {
+ if (D.getType()->isReferenceType()) {
+ if (auto *InitExprLoc =
+ Env.getStorageLocation(*InitExpr, SkipPast::Reference)) {
+ auto &Val =
+ Env.takeOwnership(std::make_unique<ReferenceValue>(*InitExprLoc));
+ Env.setValue(Loc, Val);
+ return;
+ }
+ } else {
+ if (auto *InitExprVal = Env.getValue(*InitExpr, SkipPast::None)) {
+ Env.setValue(Loc, *InitExprVal);
+ return;
+ }
+ }
+ }
+
Env.initValueInStorageLocation(Loc, D.getType());
}
Index: clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
===================================================================
--- clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
@@ -20,6 +20,7 @@
#include "clang/Analysis/FlowSensitive/Value.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
+#include "llvm/Support/ErrorHandling.h"
#include <memory>
#include <utility>
@@ -73,10 +74,10 @@
for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) {
FieldLocs.insert({Field, &createStorageLocation(Field->getType())});
}
- return DACtx->takeOwnership(
+ return takeOwnership(
std::make_unique<AggregateStorageLocation>(Type, std::move(FieldLocs)));
}
- return DACtx->takeOwnership(std::make_unique<ScalarStorageLocation>(Type));
+ return takeOwnership(std::make_unique<ScalarStorageLocation>(Type));
}
StorageLocation &Environment::createStorageLocation(const VarDecl &D) {
@@ -90,25 +91,62 @@
return Loc;
}
+StorageLocation &Environment::createStorageLocation(const Expr &E) {
+ // Evaluated expressions are always assigned the same storage locations to
+ // ensure that the environment stabilizes across loop iterations. Storage
+ // locations for evaluated expressions are stored in the analysis context.
+ if (auto *Loc = DACtx->getStorageLocation(E))
+ return *Loc;
+ auto &Loc = createStorageLocation(E.getType());
+ DACtx->setStorageLocation(E, Loc);
+ return Loc;
+}
+
void Environment::setStorageLocation(const ValueDecl &D, StorageLocation &Loc) {
assert(DeclToLoc.find(&D) == DeclToLoc.end());
DeclToLoc[&D] = &Loc;
}
-StorageLocation *Environment::getStorageLocation(const ValueDecl &D) const {
+StorageLocation *Environment::getStorageLocation(const ValueDecl &D,
+ SkipPast SP) const {
auto It = DeclToLoc.find(&D);
- return It == DeclToLoc.end() ? nullptr : It->second;
+ return It == DeclToLoc.end() ? nullptr : &skip(*It->second, SP);
+}
+
+void Environment::setStorageLocation(const Expr &E, StorageLocation &Loc) {
+ assert(ExprToLoc.find(&E) == ExprToLoc.end());
+ ExprToLoc[&E] = &Loc;
+}
+
+StorageLocation *Environment::getStorageLocation(const Expr &E,
+ SkipPast SP) const {
+ auto It = ExprToLoc.find(&E);
+ return It == ExprToLoc.end() ? nullptr : &skip(*It->second, SP);
}
void Environment::setValue(const StorageLocation &Loc, Value &Value) {
LocToVal[&Loc] = &Value;
}
-Value *Environment::getValue(const StorageLocation &Loc) const {
- auto It = LocToVal.find(&Loc);
+Value *Environment::getValue(const StorageLocation &Loc, SkipPast SP) const {
+ auto It = LocToVal.find(&skip(Loc, SP));
return It == LocToVal.end() ? nullptr : It->second;
}
+Value *Environment::getValue(const ValueDecl &D, SkipPast SP) const {
+ auto *Loc = getStorageLocation(D, SP);
+ if (Loc == nullptr)
+ return nullptr;
+ return getValue(*Loc, SkipPast::None);
+}
+
+Value *Environment::getValue(const Expr &E, SkipPast SP) const {
+ auto *Loc = getStorageLocation(E, SP);
+ if (Loc == nullptr)
+ return nullptr;
+ return getValue(*Loc, SkipPast::None);
+}
+
Value *Environment::initValueInStorageLocation(const StorageLocation &Loc,
QualType Type) {
llvm::DenseSet<QualType> Visited;
@@ -121,9 +159,9 @@
assert(!Type.isNull());
if (Type->isIntegerType()) {
- auto &Value = DACtx->takeOwnership(std::make_unique<IntegerValue>());
- setValue(Loc, Value);
- return &Value;
+ auto &Val = takeOwnership(std::make_unique<IntegerValue>());
+ setValue(Loc, Val);
+ return &Val;
}
if (Type->isReferenceType()) {
@@ -137,10 +175,9 @@
Visited.erase(PointeeType.getCanonicalType());
}
- auto &Value =
- DACtx->takeOwnership(std::make_unique<ReferenceValue>(PointeeLoc));
- setValue(Loc, Value);
- return &Value;
+ auto &Val = takeOwnership(std::make_unique<ReferenceValue>(PointeeLoc));
+ setValue(Loc, Val);
+ return &Val;
}
if (Type->isPointerType()) {
@@ -154,10 +191,9 @@
Visited.erase(PointeeType.getCanonicalType());
}
- auto &Value =
- DACtx->takeOwnership(std::make_unique<PointerValue>(PointeeLoc));
- setValue(Loc, Value);
- return &Value;
+ auto &Val = takeOwnership(std::make_unique<PointerValue>(PointeeLoc));
+ setValue(Loc, Val);
+ return &Val;
}
if (Type->isStructureOrClassType()) {
@@ -178,14 +214,45 @@
Visited.erase(FieldType.getCanonicalType());
}
- auto &Value = DACtx->takeOwnership(
- std::make_unique<StructValue>(std::move(FieldValues)));
- setValue(Loc, Value);
- return &Value;
+ auto &Val =
+ takeOwnership(std::make_unique<StructValue>(std::move(FieldValues)));
+ setValue(Loc, Val);
+ return &Val;
}
return nullptr;
}
+StorageLocation &
+Environment::takeOwnership(std::unique_ptr<StorageLocation> Loc) {
+ return DACtx->takeOwnership(std::move(Loc));
+}
+
+Value &Environment::takeOwnership(std::unique_ptr<Value> Val) {
+ return DACtx->takeOwnership(std::move(Val));
+}
+
+StorageLocation &Environment::skip(StorageLocation &Loc, SkipPast SP) const {
+ switch (SP) {
+ case SkipPast::None:
+ return Loc;
+ case SkipPast::Reference:
+ // References cannot be chained so we only need to skip past one level of
+ // indirection.
+ if (auto *Val =
+ dyn_cast_or_null<ReferenceValue>(getValue(Loc, SkipPast::None))) {
+ return Val->getPointeeLoc();
+ } else {
+ return Loc;
+ }
+ }
+ llvm_unreachable("bad SkipPast kind");
+}
+
+const StorageLocation &Environment::skip(const StorageLocation &Loc,
+ SkipPast SP) const {
+ return skip(*const_cast<StorageLocation *>(&Loc), SP);
+}
+
} // namespace dataflow
} // namespace clang
Index: clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
@@ -16,6 +16,7 @@
#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DATAFLOWENVIRONMENT_H
#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
@@ -28,6 +29,15 @@
namespace clang {
namespace dataflow {
+/// Indicates what kind of indirections should be skipped past when retrieving
+/// storage locations or values.
+enum class SkipPast {
+ /// No indirections should be skipped past.
+ None,
+ /// An optional reference should be skipped past.
+ Reference,
+};
+
/// Holds the state of the program (store and heap) at a given program point.
class Environment {
public:
@@ -50,6 +60,11 @@
/// returned storage location in the environment.
StorageLocation &createStorageLocation(const VarDecl &D);
+ /// Creates a storage location for `E`. Does not assign the returned storage
+ /// location to `E` in the environment. Does not assign a value to the
+ /// returned storage location in the environment.
+ StorageLocation &createStorageLocation(const Expr &E);
+
/// Assigns `Loc` as the storage location of `D` in the environment.
///
/// Requirements:
@@ -57,9 +72,22 @@
/// `D` must not be assigned a storage location in the environment.
void setStorageLocation(const ValueDecl &D, StorageLocation &Loc);
- /// Returns the storage location assigned to `D` in the environment or null if
- /// `D` isn't assigned a storage location in the environment.
- StorageLocation *getStorageLocation(const ValueDecl &D) const;
+ /// Returns the storage location assigned to `D` in the environment, applying
+ /// the `SP` policy for skipping past indirections, or null if `D` isn't
+ /// assigned a storage location in the environment.
+ StorageLocation *getStorageLocation(const ValueDecl &D, SkipPast SP) const;
+
+ /// Assigns `Loc` as the storage location of `E` in the environment.
+ ///
+ /// Requirements:
+ ///
+ /// `E` must not be assigned a storage location in the environment.
+ void setStorageLocation(const Expr &E, StorageLocation &Loc);
+
+ /// Returns the storage location assigned to `E` in the environment, applying
+ /// the `SP` policy for skipping past indirections, or null if `E` isn't
+ /// assigned a storage location in the environment.
+ StorageLocation *getStorageLocation(const Expr &E, SkipPast SP) const;
/// Creates a value appropriate for `Type`, assigns it to `Loc`, and returns
/// it, if `Type` is supported, otherwise return null. If `Type` is a pointer
@@ -74,9 +102,26 @@
/// Assigns `Val` as the value of `Loc` in the environment.
void setValue(const StorageLocation &Loc, Value &Val);
- /// Returns the value assigned to `Loc` in the environment or null if `Loc`
- /// isn't assigned a value in the environment.
- Value *getValue(const StorageLocation &Loc) const;
+ /// Returns the value assigned to `Loc` in the environment, applying the `SP`
+ /// policy for skipping past indirections, or null if `Loc` isn't assigned a
+ /// value in the environment.
+ Value *getValue(const StorageLocation &Loc, SkipPast SP) const;
+
+ /// Equivalent to `getValue(getStorageLocation(D, SP), SkipPast::None)` if `D`
+ /// is assigned a storage location in the environment, otherwise returns null.
+ Value *getValue(const ValueDecl &D, SkipPast SP) const;
+
+ /// Equivalent to `getValue(getStorageLocation(E, SP), SkipPast::None)` if `E`
+ /// is assigned a storage location in the environment, otherwise returns null.
+ Value *getValue(const Expr &E, SkipPast SP) const;
+
+ /// Transfers ownership of `Loc` to the analysis context and returns a
+ /// reference to `Loc`.
+ StorageLocation &takeOwnership(std::unique_ptr<StorageLocation> Loc);
+
+ /// Transfers ownership of `Val` to the analysis context and returns a
+ /// reference to `Val`.
+ Value &takeOwnership(std::unique_ptr<Value> Val);
private:
/// Returns the value assigned to `Loc` in the environment or null if `Type`
@@ -94,6 +139,10 @@
const StorageLocation &Loc, QualType Type,
llvm::DenseSet<QualType> &Visited);
+ StorageLocation &skip(StorageLocation &Loc, SkipPast SP) const;
+ const StorageLocation &skip(const StorageLocation &Loc, SkipPast SP) const;
+
+ // `DACtx` is not null and not owned by this object.
DataflowAnalysisContext *DACtx;
// Maps from program declarations and statements to storage locations that are
@@ -101,7 +150,7 @@
// include only storage locations that are in scope for a particular basic
// block.
llvm::DenseMap<const ValueDecl *, StorageLocation *> DeclToLoc;
- // FIXME: Add `Expr` to `StorageLocation` map.
+ llvm::DenseMap<const Expr *, StorageLocation *> ExprToLoc;
llvm::DenseMap<const StorageLocation *, Value *> LocToVal;
Index: clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
===================================================================
--- clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
@@ -16,6 +16,7 @@
#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DATAFLOWANALYSISCONTEXT_H
#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
#include "clang/Analysis/FlowSensitive/StorageLocation.h"
#include "clang/Analysis/FlowSensitive/Value.h"
#include "llvm/ADT/DenseMap.h"
@@ -70,6 +71,23 @@
return It == DeclToLoc.end() ? nullptr : It->second;
}
+ /// Assigns `Loc` as the storage location of `E`.
+ ///
+ /// Requirements:
+ ///
+ /// `E` must not be assigned a storage location.
+ void setStorageLocation(const Expr &E, StorageLocation &Loc) {
+ assert(ExprToLoc.find(&E) == ExprToLoc.end());
+ ExprToLoc[&E] = &Loc;
+ }
+
+ /// Returns the storage location assigned to `E` or null if `E` has no
+ /// assigned storage location.
+ StorageLocation *getStorageLocation(const Expr &E) const {
+ auto It = ExprToLoc.find(&E);
+ return It == ExprToLoc.end() ? nullptr : It->second;
+ }
+
private:
// Storage for the state of a program.
std::vector<std::unique_ptr<StorageLocation>> Locs;
@@ -81,7 +99,7 @@
// basic blocks are evaluated multiple times. The storage locations that are
// in scope for a particular basic block are stored in `Environment`.
llvm::DenseMap<const ValueDecl *, StorageLocation *> DeclToLoc;
- // FIXME: Add `Expr` to `StorageLocation` map.
+ llvm::DenseMap<const Expr *, StorageLocation *> ExprToLoc;
// FIXME: Add `StorageLocation` for `this`.
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits