[clang] 37e6496 - [clang][dataflow] Add transfer functions for bind temporary and static cast

Stanislav Gatev via cfe-commits cfe-commits at lists.llvm.org
Sun Jan 16 09:56:52 PST 2022


Author: Stanislav Gatev
Date: 2022-01-16T17:41:02Z
New Revision: 37e6496c800b33cbf6f7967d90eab53327147478

URL: https://github.com/llvm/llvm-project/commit/37e6496c800b33cbf6f7967d90eab53327147478
DIFF: https://github.com/llvm/llvm-project/commit/37e6496c800b33cbf6f7967d90eab53327147478.diff

LOG: [clang][dataflow] Add transfer functions for bind temporary and static cast

This is part of the implementation of the dataflow analysis framework.
See "[RFC] A dataflow analysis framework for Clang AST" on cfe-dev.

Differential Revision: https://reviews.llvm.org/D117339

Added: 
    

Modified: 
    clang/lib/Analysis/FlowSensitive/Transfer.cpp
    clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index 8b35ec023ecc..cc73fb09ffd9 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -87,11 +87,50 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
   }
 
   void VisitDeclStmt(const DeclStmt *S) {
-    // FIXME: Add support for group decls, e.g: `int a, b;`
-    if (S->isSingleDecl()) {
-      if (const auto *D = dyn_cast<VarDecl>(S->getSingleDecl())) {
-        visitVarDecl(*D);
+    // Group decls are converted into single decls in the CFG so the cast below
+    // is safe.
+    const auto &D = *cast<VarDecl>(S->getSingleDecl());
+    auto &Loc = Env.createStorageLocation(D);
+    Env.setStorageLocation(D, Loc);
+
+    const Expr *InitExpr = D.getInit();
+    if (InitExpr == nullptr) {
+      // No initializer expression - associate `Loc` with a new value.
+      Env.initValueInStorageLocation(Loc, D.getType());
+      return;
+    }
+
+    // The CFG does not contain `ParenExpr` as top-level statements in basic
+    // blocks, however sub-expressions can still be of that type.
+    InitExpr = skipExprWithCleanups(D.getInit()->IgnoreParens());
+    assert(InitExpr != nullptr);
+
+    if (D.getType()->isReferenceType()) {
+      // Initializing a reference variable - do not create a reference to
+      // reference.
+      if (auto *InitExprLoc =
+              Env.getStorageLocation(*InitExpr, SkipPast::Reference)) {
+        auto &Val =
+            Env.takeOwnership(std::make_unique<ReferenceValue>(*InitExprLoc));
+        Env.setValue(Loc, Val);
+      } else {
+        // FIXME: The initializer expression must always be assigned a value.
+        // Replace this with an assert when we have sufficient coverage of
+        // language features.
+        Env.initValueInStorageLocation(Loc, D.getType());
       }
+      return;
+    }
+
+    if (auto *InitExprVal = Env.getValue(*InitExpr, SkipPast::None)) {
+      Env.setValue(Loc, *InitExprVal);
+    } else if (!D.getType()->isStructureOrClassType()) {
+      // FIXME: The initializer expression must always be assigned a value.
+      // Replace this with an assert when we have sufficient coverage of
+      // language features.
+      Env.initValueInStorageLocation(Loc, D.getType());
+    } else {
+      llvm_unreachable("structs and classes must always be assigned values");
     }
   }
 
@@ -309,57 +348,34 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
     Env.setStorageLocation(*S, *SubExprLoc);
   }
 
-  // FIXME: Add support for:
-  // - CXXBindTemporaryExpr
-  // - CXXBoolLiteralExpr
-  // - CXXStaticCastExpr
-
-private:
-  void visitVarDecl(const VarDecl &D) {
-    auto &Loc = Env.createStorageLocation(D);
-    Env.setStorageLocation(D, Loc);
+  void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *S) {
+    const Expr *SubExpr = S->getSubExpr();
+    assert(SubExpr != nullptr);
 
-    const Expr *InitExpr = D.getInit();
-    if (InitExpr == nullptr) {
-      // No initializer expression - associate `Loc` with a new value.
-      Env.initValueInStorageLocation(Loc, D.getType());
+    auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None);
+    if (SubExprLoc == nullptr)
       return;
-    }
 
-    // The CFG does not contain `ParenExpr` as top-level statements in basic
-    // blocks, however sub-expressions can still be of that type.
-    InitExpr = skipExprWithCleanups(D.getInit()->IgnoreParens());
-    assert(InitExpr != nullptr);
+    Env.setStorageLocation(*S, *SubExprLoc);
+  }
 
-    if (D.getType()->isReferenceType()) {
-      // Initializing a reference variable - do not create a reference to
-      // reference.
-      if (auto *InitExprLoc =
-              Env.getStorageLocation(*InitExpr, SkipPast::Reference)) {
-        auto &Val =
-            Env.takeOwnership(std::make_unique<ReferenceValue>(*InitExprLoc));
-        Env.setValue(Loc, Val);
-      } else {
-        // FIXME: The initializer expression must always be assigned a value.
-        // Replace this with an assert when we have sufficient coverage of
-        // language features.
-        Env.initValueInStorageLocation(Loc, D.getType());
-      }
-      return;
-    }
+  void VisitCXXStaticCastExpr(const CXXStaticCastExpr *S) {
+    if (S->getCastKind() == CK_NoOp) {
+      const Expr *SubExpr = S->getSubExpr();
+      assert(SubExpr != nullptr);
 
-    if (auto *InitExprVal = Env.getValue(*InitExpr, SkipPast::None)) {
-      Env.setValue(Loc, *InitExprVal);
-    } else if (!D.getType()->isStructureOrClassType()) {
-      // FIXME: The initializer expression must always be assigned a value.
-      // Replace this with an assert when we have sufficient coverage of
-      // language features.
-      Env.initValueInStorageLocation(Loc, D.getType());
-    } else {
-      llvm_unreachable("structs and classes must always be assigned values");
+      auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None);
+      if (SubExprLoc == nullptr)
+        return;
+
+      Env.setStorageLocation(*S, *SubExprLoc);
     }
   }
 
+  // FIXME: Add support for:
+  // - CXXBoolLiteralExpr
+
+private:
   Environment &Env;
 };
 

diff  --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index ac97cce8a956..e50f032a3c1a 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -489,6 +489,44 @@ TEST_F(TransferTest, SelfReferentialPointerVarDecl) {
       });
 }
 
+TEST_F(TransferTest, MultipleVarsDecl) {
+  std::string Code = R"(
+    void target() {
+      int Foo, Bar;
+      (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 ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+                ASSERT_THAT(BarDecl, NotNull());
+
+                const StorageLocation *FooLoc =
+                    Env.getStorageLocation(*FooDecl, SkipPast::None);
+                ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
+
+                const StorageLocation *BarLoc =
+                    Env.getStorageLocation(*BarDecl, SkipPast::None);
+                ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
+
+                const Value *FooVal = Env.getValue(*FooLoc);
+                EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
+
+                const Value *BarVal = Env.getValue(*BarLoc);
+                EXPECT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
+              });
+}
+
 TEST_F(TransferTest, JoinVarDecl) {
   std::string Code = R"(
     void target(bool B) {
@@ -1589,4 +1627,71 @@ TEST_F(TransferTest, MoveConstructor) {
       });
 }
 
+TEST_F(TransferTest, BindTemporary) {
+  std::string Code = R"(
+    struct A {
+      virtual ~A() = default;
+
+      int Baz;
+    };
+
+    void target(A Foo) {
+      int Bar = A(Foo).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 ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo");
+                ASSERT_THAT(FooDecl, NotNull());
+
+                const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+                ASSERT_THAT(BarDecl, NotNull());
+
+                const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz");
+                ASSERT_THAT(BazDecl, NotNull());
+
+                const auto &FooVal =
+                    *cast<StructValue>(Env.getValue(*FooDecl, SkipPast::None));
+                const auto *BarVal =
+                    cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
+                EXPECT_EQ(BarVal, &FooVal.getChild(*BazDecl));
+              });
+}
+
+TEST_F(TransferTest, StaticCast) {
+  std::string Code = R"(
+    void target(int Foo) {
+      int Bar = static_cast<int>(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 ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar");
+                ASSERT_THAT(BarDecl, NotNull());
+
+                const auto *FooVal =
+                    cast<IntegerValue>(Env.getValue(*FooDecl, SkipPast::None));
+                const auto *BarVal =
+                    cast<IntegerValue>(Env.getValue(*BarDecl, SkipPast::None));
+                EXPECT_EQ(FooVal, BarVal);
+              });
+}
+
 } // namespace


        


More information about the cfe-commits mailing list