[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