[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