[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