[clang] 963f400 - [clang][dataflow] Add transfer functions for initializers
Stanislav Gatev via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 12 13:52:31 PST 2022
Author: Stanislav Gatev
Date: 2022-01-12T21:51:39Z
New Revision: 963f40051a4216936b47dad56765d60b64b7840d
URL: https://github.com/llvm/llvm-project/commit/963f40051a4216936b47dad56765d60b64b7840d
DIFF: https://github.com/llvm/llvm-project/commit/963f40051a4216936b47dad56765d60b64b7840d.diff
LOG: [clang][dataflow] Add transfer functions for initializers
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/D117123
Added:
Modified:
clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp
clang/lib/Analysis/FlowSensitive/Transfer.cpp
clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Removed:
################################################################################
diff --git a/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp b/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp
index a1817687bd68e..3ad2ed640cc1c 100644
--- a/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp
+++ b/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp
@@ -49,6 +49,7 @@ ControlFlowContext::build(const Decl *D, Stmt *S, ASTContext *C) {
Options.AddImplicitDtors = true;
Options.AddTemporaryDtors = true;
Options.AddInitializers = true;
+ Options.AddCXXDefaultInitExprInCtors = true;
// Ensure that all sub-expressions in basic blocks are evaluated.
Options.setAllAlwaysAdd();
diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index f28271101b9c1..7681b729d2b8e 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -164,6 +164,23 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
}
}
+ void VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *S) {
+ const Expr *InitExpr = S->getExpr();
+ assert(InitExpr != nullptr);
+
+ Value *InitExprVal = Env.getValue(*InitExpr, SkipPast::None);
+ if (InitExprVal == nullptr)
+ return;
+
+ const FieldDecl *Field = S->getField();
+ assert(Field != nullptr);
+
+ auto &ThisLoc =
+ *cast<AggregateStorageLocation>(Env.getThisPointeeStorageLocation());
+ auto &FieldLoc = ThisLoc.getChild(*Field);
+ Env.setValue(FieldLoc, *InitExprVal);
+ }
+
// FIXME: Add support for:
// - CallExpr
// - CXXBindTemporaryExpr
diff --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
index 0d7f3b31e6c48..722b24f5b9718 100644
--- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
+++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp
@@ -11,15 +11,18 @@
//
//===----------------------------------------------------------------------===//
+#include <memory>
#include <utility>
#include <vector>
+#include "clang/AST/DeclCXX.h"
#include "clang/Analysis/Analyses/PostOrderCFGView.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
#include "clang/Analysis/FlowSensitive/Transfer.h"
#include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h"
+#include "clang/Analysis/FlowSensitive/Value.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/raw_ostream.h"
@@ -103,6 +106,60 @@ static TypeErasedDataflowAnalysisState computeBlockInputState(
return *MaybeState;
}
+/// Transfers `State` by evaluating `CfgStmt` in the context of `Analysis`.
+/// `HandleTransferredStmt` (if provided) will be applied to `CfgStmt`, after it
+/// is evaluated.
+static void
+transferCFGStmt(const CFGStmt &CfgStmt, TypeErasedDataflowAnalysis &Analysis,
+ TypeErasedDataflowAnalysisState &State,
+ std::function<void(const CFGStmt &,
+ const TypeErasedDataflowAnalysisState &)>
+ HandleTransferredStmt) {
+ const Stmt *S = CfgStmt.getStmt();
+ assert(S != nullptr);
+
+ transfer(*S, State.Env);
+ Analysis.transferTypeErased(S, State.Lattice, State.Env);
+
+ if (HandleTransferredStmt != nullptr)
+ HandleTransferredStmt(CfgStmt, State);
+}
+
+/// Transfers `State` by evaluating `CfgInit`.
+static void transferCFGInitializer(const CFGInitializer &CfgInit,
+ TypeErasedDataflowAnalysisState &State) {
+ const auto &ThisLoc = *cast<AggregateStorageLocation>(
+ State.Env.getThisPointeeStorageLocation());
+
+ const CXXCtorInitializer *Initializer = CfgInit.getInitializer();
+ assert(Initializer != nullptr);
+
+ auto *InitStmt = Initializer->getInit();
+ assert(InitStmt != nullptr);
+
+ auto *InitStmtLoc =
+ State.Env.getStorageLocation(*InitStmt, SkipPast::Reference);
+ if (InitStmtLoc == nullptr)
+ return;
+
+ auto *InitStmtVal = State.Env.getValue(*InitStmtLoc);
+ if (InitStmtVal == nullptr)
+ return;
+
+ const FieldDecl *Member = Initializer->getMember();
+ assert(Member != nullptr);
+
+ if (Member->getType()->isReferenceType()) {
+ auto &MemberLoc = ThisLoc.getChild(*Member);
+ State.Env.setValue(MemberLoc,
+ State.Env.takeOwnership(
+ std::make_unique<ReferenceValue>(*InitStmtLoc)));
+ } else {
+ auto &MemberLoc = ThisLoc.getChild(*Member);
+ State.Env.setValue(MemberLoc, *InitStmtVal);
+ }
+}
+
TypeErasedDataflowAnalysisState transferBlock(
const ControlFlowContext &CFCtx,
std::vector<llvm::Optional<TypeErasedDataflowAnalysisState>> &BlockStates,
@@ -114,19 +171,18 @@ TypeErasedDataflowAnalysisState transferBlock(
TypeErasedDataflowAnalysisState State =
computeBlockInputState(CFCtx, BlockStates, Block, InitEnv, Analysis);
for (const CFGElement &Element : Block) {
- // FIXME: Evaluate other kinds of `CFGElement`.
- const llvm::Optional<CFGStmt> CfgStmt = Element.getAs<CFGStmt>();
- if (!CfgStmt.hasValue())
- continue;
-
- const Stmt *S = CfgStmt.getValue().getStmt();
- assert(S != nullptr);
-
- transfer(*S, State.Env);
- Analysis.transferTypeErased(S, State.Lattice, State.Env);
-
- if (HandleTransferredStmt != nullptr)
- HandleTransferredStmt(CfgStmt.getValue(), State);
+ switch (Element.getKind()) {
+ case CFGElement::Statement:
+ transferCFGStmt(*Element.getAs<CFGStmt>(), Analysis, State,
+ HandleTransferredStmt);
+ break;
+ case CFGElement::Initializer:
+ transferCFGInitializer(*Element.getAs<CFGInitializer>(), State);
+ break;
+ default:
+ // FIXME: Evaluate other kinds of `CFGElement`.
+ break;
+ }
}
return State;
}
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index 55e15e28ce5eb..1c5e49df8da9a 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -1171,4 +1171,114 @@ TEST_F(TransferTest, ClassThisMember) {
});
}
+TEST_F(TransferTest, ConstructorInitializer) {
+ std::string Code = R"(
+ struct target {
+ int Bar;
+
+ target(int Foo) : Bar(Foo) {
+ int Qux = Bar;
+ // [[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 auto *ThisLoc = dyn_cast<AggregateStorageLocation>(
+ Env.getThisPointeeStorageLocation());
+ ASSERT_THAT(ThisLoc, NotNull());
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const auto *FooVal =
+ cast<IntegerValue>(Env.getValue(*FooDecl, SkipPast::None));
+
+ const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
+ ASSERT_THAT(QuxDecl, NotNull());
+ EXPECT_EQ(Env.getValue(*QuxDecl, SkipPast::None), FooVal);
+ });
+}
+
+TEST_F(TransferTest, DefaultInitializer) {
+ std::string Code = R"(
+ struct target {
+ int Bar;
+ int Baz = Bar;
+
+ target(int Foo) : Bar(Foo) {
+ int Qux = Baz;
+ // [[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 auto *ThisLoc = dyn_cast<AggregateStorageLocation>(
+ Env.getThisPointeeStorageLocation());
+ ASSERT_THAT(ThisLoc, NotNull());
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const auto *FooVal =
+ cast<IntegerValue>(Env.getValue(*FooDecl, SkipPast::None));
+
+ const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
+ ASSERT_THAT(QuxDecl, NotNull());
+ EXPECT_EQ(Env.getValue(*QuxDecl, SkipPast::None), FooVal);
+ });
+}
+
+TEST_F(TransferTest, DefaultInitializerReference) {
+ std::string Code = R"(
+ struct target {
+ int &Bar;
+ int &Baz = Bar;
+
+ target(int &Foo) : Bar(Foo) {
+ int &Qux = Baz;
+ // [[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 auto *ThisLoc = dyn_cast<AggregateStorageLocation>(
+ Env.getThisPointeeStorageLocation());
+ ASSERT_THAT(ThisLoc, NotNull());
+
+ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+ ASSERT_THAT(FooDecl, NotNull());
+
+ const auto *FooVal =
+ cast<ReferenceValue>(Env.getValue(*FooDecl, SkipPast::None));
+
+ const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
+ ASSERT_THAT(QuxDecl, NotNull());
+
+ const auto *QuxVal =
+ cast<ReferenceValue>(Env.getValue(*QuxDecl, SkipPast::None));
+ EXPECT_EQ(&QuxVal->getPointeeLoc(), &FooVal->getPointeeLoc());
+ });
+}
+
} // namespace
More information about the cfe-commits
mailing list