[clang] e7481f6 - [clang][dataflow] Add transfer functions for assignment

Stanislav Gatev via cfe-commits cfe-commits at lists.llvm.org
Mon Jan 10 11:37:18 PST 2022


Author: Stanislav Gatev
Date: 2022-01-10T19:35:50Z
New Revision: e7481f6ee591678e3a70569c2e87597f148adb3e

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

LOG: [clang][dataflow] Add transfer functions for assignment

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/D116596

Added: 
    

Modified: 
    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

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
index a99d32df018b0..8a836713e3392 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
+++ b/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 @@ class DataflowAnalysisContext {
     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 @@ class DataflowAnalysisContext {
   // 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`.
 

diff  --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
index 2a0ad6dad1239..2138e0fb6da63 100644
--- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h
+++ b/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,18 @@
 namespace clang {
 namespace dataflow {
 
+/// Indicates what kind of indirections should be skipped past when retrieving
+/// storage locations or values.
+///
+/// FIXME: Consider renaming this or replacing it with a more appropriate model.
+/// See the discussion in https://reviews.llvm.org/D116596 for context.
+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 +63,11 @@ class Environment {
   /// 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 +75,22 @@ class Environment {
   ///  `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
@@ -78,6 +109,22 @@ class Environment {
   /// isn't assigned a value in the environment.
   Value *getValue(const StorageLocation &Loc) 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`
   /// isn't supported.
@@ -94,6 +141,10 @@ class Environment {
       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 +152,7 @@ class Environment {
   // 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;
 

diff  --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
index 4d1e5477422ea..d1e7b2a4b38c5 100644
--- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
+++ b/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 @@ StorageLocation &Environment::createStorageLocation(QualType Type) {
     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,14 +91,37 @@ StorageLocation &Environment::createStorageLocation(const VarDecl &D) {
   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) {
@@ -109,6 +133,20 @@ Value *Environment::getValue(const StorageLocation &Loc) const {
   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);
+}
+
+Value *Environment::getValue(const Expr &E, SkipPast SP) const {
+  auto *Loc = getStorageLocation(E, SP);
+  if (Loc == nullptr)
+    return nullptr;
+  return getValue(*Loc);
+}
+
 Value *Environment::initValueInStorageLocation(const StorageLocation &Loc,
                                                QualType Type) {
   llvm::DenseSet<QualType> Visited;
@@ -121,9 +159,9 @@ Value *Environment::initValueInStorageLocationUnlessSelfReferential(
   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 @@ Value *Environment::initValueInStorageLocationUnlessSelfReferential(
       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 @@ Value *Environment::initValueInStorageLocationUnlessSelfReferential(
       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,42 @@ Value *Environment::initValueInStorageLocationUnlessSelfReferential(
       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)))
+      return Val->getPointeeLoc();
+    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

diff  --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
index 80005d1de9a1b..bae162ae3cd14 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/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,53 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
 public:
   TransferVisitor(Environment &Env) : Env(Env) {}
 
+  void VisitBinaryOperator(const BinaryOperator *S) {
+    if (S->getOpcode() == BO_Assign) {
+      // The CFG does not contain `ParenExpr` as top-level statements in basic
+      // blocks, however sub-expressions can still be of that type.
+      assert(S->getLHS() != nullptr);
+      const Expr *LHS = S->getLHS()->IgnoreParens();
+
+      assert(LHS != nullptr);
+      auto *LHSLoc = Env.getStorageLocation(*LHS, SkipPast::Reference);
+      if (LHSLoc == nullptr)
+        return;
+
+      // The CFG does not contain `ParenExpr` as top-level statements in basic
+      // blocks, however sub-expressions can still be of that type.
+      assert(S->getRHS() != nullptr);
+      const Expr *RHS = S->getRHS()->IgnoreParens();
+
+      assert(RHS != nullptr);
+      Value *RHSVal = Env.getValue(*RHS, SkipPast::Reference);
+      if (RHSVal == nullptr)
+        return;
+
+      // Assign a value to the storage location of the left-hand side.
+      Env.setValue(*LHSLoc, *RHSVal);
+
+      // Assign a storage location for the whole expression.
+      Env.setStorageLocation(*S, *LHSLoc);
+    }
+    // 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 +87,43 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
     }
   }
 
+  void VisitImplicitCastExpr(const ImplicitCastExpr *S) {
+    if (S->getCastKind() == CK_LValueToRValue) {
+      // The CFG does not contain `ParenExpr` as top-level statements in basic
+      // blocks, however sub-expressions can still be of that type.
+      assert(S->getSubExpr() != nullptr);
+      const Expr *SubExpr = S->getSubExpr()->IgnoreParens();
+
+      assert(SubExpr != nullptr);
+      auto *SubExprVal = Env.getValue(*SubExpr, 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.
+  }
+
+  void VisitUnaryOperator(const UnaryOperator *S) {
+    if (S->getOpcode() == UO_Deref) {
+      assert(S->getSubExpr() != nullptr);
+      const auto *SubExprVal = cast_or_null<PointerValue>(
+          Env.getValue(*S->getSubExpr(), SkipPast::Reference));
+      if (SubExprVal == nullptr)
+        return;
+
+      auto &Loc = Env.createStorageLocation(*S);
+      Env.setStorageLocation(*S, Loc);
+      Env.setValue(Loc, Env.takeOwnership(std::make_unique<ReferenceValue>(
+                            SubExprVal->getPointeeLoc())));
+    }
+    // FIXME: Add support for UO_AddrOf, UO_LNot.
+  }
+
   // FIXME: Add support for:
-  // - BinaryOperator
   // - CallExpr
   // - CXXBindTemporaryExpr
   // - CXXBoolLiteralExpr
@@ -47,17 +132,46 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
   // - CXXOperatorCallExpr
   // - CXXStaticCastExpr
   // - CXXThisExpr
-  // - DeclRefExpr
-  // - ImplicitCastExpr
   // - MaterializeTemporaryExpr
   // - MemberExpr
-  // - UnaryOperator
 
 private:
   void visitVarDecl(const VarDecl &D) {
     auto &Loc = Env.createStorageLocation(D);
     Env.setStorageLocation(D, Loc);
-    Env.initValueInStorageLocation(Loc, D.getType());
+
+    const Expr *InitExpr = D.getInit();
+    if (InitExpr == nullptr) {
+      // No initializer expression - associate `Loc` with a new value.
+      Env.initValueInStorageLocation(Loc, D.getType());
+      return;
+    }
+
+    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 {
+      // 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());
+    }
   }
 
   Environment &Env;

diff  --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index 1f8d6392355e4..a54dceb5c5b60 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -82,7 +82,8 @@ TEST_F(TransferTest, IntVarDecl) {
         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);
@@ -125,8 +126,8 @@ TEST_F(TransferTest, StructVarDecl) {
         }
         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));
 
@@ -171,8 +172,8 @@ TEST_F(TransferTest, ClassVarDecl) {
         }
         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));
 
@@ -204,7 +205,8 @@ TEST_F(TransferTest, ReferenceVarDecl) {
         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 =
@@ -293,8 +295,8 @@ TEST_F(TransferTest, SelfReferentialReferenceVarDecl) {
     ASSERT_THAT(BazRefDecl, NotNull());
     ASSERT_THAT(BazPtrDecl, NotNull());
 
-    const auto *FooLoc =
-        cast<ScalarStorageLocation>(Env.getStorageLocation(*FooDecl));
+    const auto *FooLoc = cast<ScalarStorageLocation>(
+        Env.getStorageLocation(*FooDecl, SkipPast::None));
     const auto *FooVal = cast<ReferenceValue>(Env.getValue(*FooLoc));
     const auto *FooPointeeVal =
         cast<StructValue>(Env.getValue(FooVal->getPointeeLoc()));
@@ -348,7 +350,8 @@ TEST_F(TransferTest, PointerVarDecl) {
         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));
@@ -449,8 +452,8 @@ TEST_F(TransferTest, SelfReferentialPointerVarDecl) {
         ASSERT_THAT(BazRefDecl, NotNull());
         ASSERT_THAT(BazPtrDecl, NotNull());
 
-        const auto *FooLoc =
-            cast<ScalarStorageLocation>(Env.getStorageLocation(*FooDecl));
+        const auto *FooLoc = cast<ScalarStorageLocation>(
+            Env.getStorageLocation(*FooDecl, SkipPast::None));
         const auto *FooVal = cast<PointerValue>(Env.getValue(*FooLoc));
         const auto *FooPointeeVal =
             cast<StructValue>(Env.getValue(FooVal->getPointeeLoc()));
@@ -498,42 +501,225 @@ TEST_F(TransferTest, JoinVarDecl) {
       // [[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());
+
+    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, 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());
+
+                EXPECT_EQ(Env.getValue(*BarDecl, SkipPast::None), 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());
+
+                EXPECT_EQ(Env.getValue(*BarDecl, SkipPast::None), FooVal);
+              });
+}
+
+TEST_F(TransferTest, VarDeclInitAssignChained) {
+  std::string Code = R"(
+    void target() {
+      int foo;
+      int bar;
+      int baz = (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 ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz");
+                ASSERT_THAT(BazDecl, NotNull());
+
+                EXPECT_EQ(Env.getValue(*BarDecl, SkipPast::None), FooVal);
+                EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), FooVal);
+              });
+}
+
+TEST_F(TransferTest, VarDeclInitAssignPtrDeref) {
+  std::string Code = R"(
+    void target() {
+      int foo;
+      int *bar;
+      *(bar) = foo;
+      int baz = *(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 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 auto *BarVal =
+                    cast<PointerValue>(Env.getValue(*BarDecl, SkipPast::None));
+                EXPECT_EQ(Env.getValue(BarVal->getPointeeLoc()), FooVal);
+
+                const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz");
+                ASSERT_THAT(BazDecl, NotNull());
+
+                EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), FooVal);
+              });
+}
+
+TEST_F(TransferTest, AssignToAndFromReference) {
+  std::string Code = R"(
+    void target() {
+      int foo;
+      int bar;
+      int& baz = foo;
+      // [[p1]]
+      baz = bar;
+      int qux = baz;
+      int& quux = baz;
+      // [[p2]]
+    }
+  )";
   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", _)));
+        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());
 
-        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());
+        EXPECT_EQ(Env1.getValue(*BazDecl, SkipPast::Reference), FooVal);
+
+        EXPECT_EQ(Env2.getValue(*BazDecl, SkipPast::Reference), BarVal);
+        EXPECT_EQ(Env2.getValue(*FooDecl, SkipPast::None), BarVal);
+
+        const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "qux");
+        ASSERT_THAT(QuxDecl, NotNull());
+        EXPECT_EQ(Env2.getValue(*QuxDecl, SkipPast::None), BarVal);
+
+        const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, "quux");
+        ASSERT_THAT(QuuxDecl, NotNull());
+        EXPECT_EQ(Env2.getValue(*QuuxDecl, SkipPast::Reference), BarVal);
       });
 }
 


        


More information about the cfe-commits mailing list