[clang] 0e286b7 - [clang][dataflow] Add transfer functions for structured bindings
Stanislav Gatev via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 2 01:03:39 PDT 2022
Author: Stanislav Gatev
Date: 2022-06-02T08:02:26Z
New Revision: 0e286b77cf7bebca568b60ac81c1717291040bc3
URL: https://github.com/llvm/llvm-project/commit/0e286b77cf7bebca568b60ac81c1717291040bc3
DIFF: https://github.com/llvm/llvm-project/commit/0e286b77cf7bebca568b60ac81c1717291040bc3.diff
LOG: [clang][dataflow] Add transfer functions for structured bindings
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/D120495
Reviewed-by: ymandel, xazax.hun
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 fc04e0b55ffc7..a56e277f7962f 100644
--- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp
@@ -136,9 +136,6 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
return;
}
- InitExpr = D.getInit();
- assert(InitExpr != nullptr);
-
if (D.getType()->isReferenceType()) {
// Initializing a reference variable - do not create a reference to
// reference.
@@ -147,25 +144,52 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
auto &Val =
Env.takeOwnership(std::make_unique<ReferenceValue>(*InitExprLoc));
Env.setValue(Loc, Val);
- return;
}
} else if (auto *InitExprVal = Env.getValue(*InitExpr, SkipPast::None)) {
Env.setValue(Loc, *InitExprVal);
- return;
}
- // We arrive here in (the few) cases where an expression is intentionally
- // "uninterpreted". There are two ways to handle this situation: propagate
- // the status, so that uninterpreted initializers result in uninterpreted
- // variables, or provide a default value. We choose the latter so that later
- // refinements of the variable can be used for reasoning about the
- // surrounding code.
- //
- // FIXME. If and when we interpret all language cases, change this to assert
- // that `InitExpr` is interpreted, rather than supplying a default value
- // (assuming we don't update the environment API to return references).
- if (Value *Val = Env.createValue(D.getType()))
- Env.setValue(Loc, *Val);
+ if (Env.getValue(Loc) == nullptr) {
+ // We arrive here in (the few) cases where an expression is intentionally
+ // "uninterpreted". There are two ways to handle this situation: propagate
+ // the status, so that uninterpreted initializers result in uninterpreted
+ // variables, or provide a default value. We choose the latter so that
+ // later refinements of the variable can be used for reasoning about the
+ // surrounding code.
+ //
+ // FIXME. If and when we interpret all language cases, change this to
+ // assert that `InitExpr` is interpreted, rather than supplying a default
+ // value (assuming we don't update the environment API to return
+ // references).
+ if (Value *Val = Env.createValue(D.getType()))
+ Env.setValue(Loc, *Val);
+ }
+
+ if (const auto *Decomp = dyn_cast<DecompositionDecl>(&D)) {
+ // If VarDecl is a DecompositionDecl, evaluate each of its bindings. This
+ // needs to be evaluated after initializing the values in the storage for
+ // VarDecl, as the bindings refer to them.
+ // FIXME: Add support for ArraySubscriptExpr.
+ // FIXME: Consider adding AST nodes that are used for structured bindings
+ // to the CFG.
+ for (const auto *B : Decomp->bindings()) {
+ auto *ME = dyn_cast_or_null<MemberExpr>(B->getBinding());
+ if (ME == nullptr)
+ continue;
+
+ auto *DE = dyn_cast_or_null<DeclRefExpr>(ME->getBase());
+ if (DE == nullptr)
+ continue;
+
+ // ME and its base haven't been visited because they aren't included in
+ // the statements of the CFG basic block.
+ VisitDeclRefExpr(DE);
+ VisitMemberExpr(ME);
+
+ if (auto *Loc = Env.getStorageLocation(*ME, SkipPast::Reference))
+ Env.setStorageLocation(*B, *Loc);
+ }
+ }
}
void VisitImplicitCastExpr(const ImplicitCastExpr *S) {
diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
index efea7797b45fb..a8552161b70d0 100644
--- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
@@ -3369,4 +3369,192 @@ TEST_F(TransferTest, DoesNotCrashOnUnionThisExpr) {
LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator=");
}
+TEST_F(TransferTest, StructuredBindingAssignFromStructIntMembersToRefs) {
+ std::string Code = R"(
+ struct A {
+ int Foo;
+ int Bar;
+ };
+
+ void target() {
+ int Qux;
+ A Baz;
+ Baz.Foo = Qux;
+ auto &FooRef = Baz.Foo;
+ auto &BarRef = Baz.Bar;
+ auto &[BoundFooRef, BoundBarRef] = 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 *FooRefDecl = findValueDecl(ASTCtx, "FooRef");
+ ASSERT_THAT(FooRefDecl, NotNull());
+
+ const ValueDecl *BarRefDecl = findValueDecl(ASTCtx, "BarRef");
+ ASSERT_THAT(BarRefDecl, NotNull());
+
+ const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
+ ASSERT_THAT(QuxDecl, NotNull());
+
+ const ValueDecl *BoundFooRefDecl = findValueDecl(ASTCtx, "BoundFooRef");
+ ASSERT_THAT(BoundFooRefDecl, NotNull());
+
+ const ValueDecl *BoundBarRefDecl = findValueDecl(ASTCtx, "BoundBarRef");
+ ASSERT_THAT(BoundBarRefDecl, NotNull());
+
+ const StorageLocation *FooRefLoc =
+ Env.getStorageLocation(*FooRefDecl, SkipPast::Reference);
+ ASSERT_THAT(FooRefLoc, NotNull());
+
+ const StorageLocation *BarRefLoc =
+ Env.getStorageLocation(*BarRefDecl, SkipPast::Reference);
+ ASSERT_THAT(BarRefLoc, NotNull());
+
+ const Value *QuxVal = Env.getValue(*QuxDecl, SkipPast::None);
+ ASSERT_THAT(QuxVal, NotNull());
+
+ const StorageLocation *BoundFooRefLoc =
+ Env.getStorageLocation(*BoundFooRefDecl, SkipPast::Reference);
+ EXPECT_EQ(BoundFooRefLoc, FooRefLoc);
+
+ const StorageLocation *BoundBarRefLoc =
+ Env.getStorageLocation(*BoundBarRefDecl, SkipPast::Reference);
+ EXPECT_EQ(BoundBarRefLoc, BarRefLoc);
+
+ EXPECT_EQ(Env.getValue(*BoundFooRefDecl, SkipPast::Reference), QuxVal);
+ });
+}
+
+TEST_F(TransferTest, StructuredBindingAssignFromStructRefMembersToRefs) {
+ std::string Code = R"(
+ struct A {
+ int &Foo;
+ int &Bar;
+ };
+
+ void target(A Baz) {
+ int Qux;
+ Baz.Foo = Qux;
+ auto &FooRef = Baz.Foo;
+ auto &BarRef = Baz.Bar;
+ auto &[BoundFooRef, BoundBarRef] = 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 *FooRefDecl = findValueDecl(ASTCtx, "FooRef");
+ ASSERT_THAT(FooRefDecl, NotNull());
+
+ const ValueDecl *BarRefDecl = findValueDecl(ASTCtx, "BarRef");
+ ASSERT_THAT(BarRefDecl, NotNull());
+
+ const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
+ ASSERT_THAT(QuxDecl, NotNull());
+
+ const ValueDecl *BoundFooRefDecl = findValueDecl(ASTCtx, "BoundFooRef");
+ ASSERT_THAT(BoundFooRefDecl, NotNull());
+
+ const ValueDecl *BoundBarRefDecl = findValueDecl(ASTCtx, "BoundBarRef");
+ ASSERT_THAT(BoundBarRefDecl, NotNull());
+
+ const StorageLocation *FooRefLoc =
+ Env.getStorageLocation(*FooRefDecl, SkipPast::Reference);
+ ASSERT_THAT(FooRefLoc, NotNull());
+
+ const StorageLocation *BarRefLoc =
+ Env.getStorageLocation(*BarRefDecl, SkipPast::Reference);
+ ASSERT_THAT(BarRefLoc, NotNull());
+
+ const Value *QuxVal = Env.getValue(*QuxDecl, SkipPast::None);
+ ASSERT_THAT(QuxVal, NotNull());
+
+ const StorageLocation *BoundFooRefLoc =
+ Env.getStorageLocation(*BoundFooRefDecl, SkipPast::Reference);
+ EXPECT_EQ(BoundFooRefLoc, FooRefLoc);
+
+ const StorageLocation *BoundBarRefLoc =
+ Env.getStorageLocation(*BoundBarRefDecl, SkipPast::Reference);
+ EXPECT_EQ(BoundBarRefLoc, BarRefLoc);
+
+ EXPECT_EQ(Env.getValue(*BoundFooRefDecl, SkipPast::Reference), QuxVal);
+ });
+}
+
+TEST_F(TransferTest, StructuredBindingAssignFromStructIntMembersToInts) {
+ std::string Code = R"(
+ struct A {
+ int Foo;
+ int Bar;
+ };
+
+ void target() {
+ int Qux;
+ A Baz;
+ Baz.Foo = Qux;
+ auto &FooRef = Baz.Foo;
+ auto &BarRef = Baz.Bar;
+ auto [BoundFoo, BoundBar] = 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 *FooRefDecl = findValueDecl(ASTCtx, "FooRef");
+ ASSERT_THAT(FooRefDecl, NotNull());
+
+ const ValueDecl *BarRefDecl = findValueDecl(ASTCtx, "BarRef");
+ ASSERT_THAT(BarRefDecl, NotNull());
+
+ const ValueDecl *BoundFooDecl = findValueDecl(ASTCtx, "BoundFoo");
+ ASSERT_THAT(BoundFooDecl, NotNull());
+
+ const ValueDecl *BoundBarDecl = findValueDecl(ASTCtx, "BoundBar");
+ ASSERT_THAT(BoundBarDecl, NotNull());
+
+ const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux");
+ ASSERT_THAT(QuxDecl, NotNull());
+
+ const StorageLocation *FooRefLoc =
+ Env.getStorageLocation(*FooRefDecl, SkipPast::Reference);
+ ASSERT_THAT(FooRefLoc, NotNull());
+
+ const StorageLocation *BarRefLoc =
+ Env.getStorageLocation(*BarRefDecl, SkipPast::Reference);
+ ASSERT_THAT(BarRefLoc, NotNull());
+
+ const Value *QuxVal = Env.getValue(*QuxDecl, SkipPast::None);
+ ASSERT_THAT(QuxVal, NotNull());
+
+ const StorageLocation *BoundFooLoc =
+ Env.getStorageLocation(*BoundFooDecl, SkipPast::Reference);
+ EXPECT_NE(BoundFooLoc, FooRefLoc);
+
+ const StorageLocation *BoundBarLoc =
+ Env.getStorageLocation(*BoundBarDecl, SkipPast::Reference);
+ EXPECT_NE(BoundBarLoc, BarRefLoc);
+
+ EXPECT_EQ(Env.getValue(*BoundFooDecl, SkipPast::Reference), QuxVal);
+ });
+}
+
} // namespace
More information about the cfe-commits
mailing list