From 963f40051a4216936b47dad56765d60b64b7840d Mon Sep 17 00:00:00 2001 From: Stanislav Gatev Date: Wed, 12 Jan 2022 16:28:59 +0000 Subject: [PATCH] [clang][dataflow] Add transfer functions for initializers This is part of the implementation of the dataflow analysis framework. See "[RFC] A dataflow analysis framework for Clang AST" on cfe-dev. Reviewed-by: ymandel, xazax.hun Differential Revision: https://reviews.llvm.org/D117123 --- .../Analysis/FlowSensitive/ControlFlowContext.cpp | 1 + clang/lib/Analysis/FlowSensitive/Transfer.cpp | 17 ++++ .../FlowSensitive/TypeErasedDataflowAnalysis.cpp | 82 ++++++++++++--- .../Analysis/FlowSensitive/TransferTest.cpp | 110 +++++++++++++++++++++ 4 files changed, 197 insertions(+), 13 deletions(-) diff --git a/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp b/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp index a181768..3ad2ed6 100644 --- a/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp +++ b/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp @@ -49,6 +49,7 @@ ControlFlowContext::build(const Decl *D, Stmt *S, ASTContext *C) { Options.AddImplicitDtors = true; Options.AddTemporaryDtors = true; Options.AddInitializers = true; + Options.AddCXXDefaultInitExprInCtors = true; // Ensure that all sub-expressions in basic blocks are evaluated. Options.setAllAlwaysAdd(); diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp index f282711..7681b72 100644 --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -164,6 +164,23 @@ public: } } + void VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *S) { + const Expr *InitExpr = S->getExpr(); + assert(InitExpr != nullptr); + + Value *InitExprVal = Env.getValue(*InitExpr, SkipPast::None); + if (InitExprVal == nullptr) + return; + + const FieldDecl *Field = S->getField(); + assert(Field != nullptr); + + auto &ThisLoc = + *cast(Env.getThisPointeeStorageLocation()); + auto &FieldLoc = ThisLoc.getChild(*Field); + Env.setValue(FieldLoc, *InitExprVal); + } + // FIXME: Add support for: // - CallExpr // - CXXBindTemporaryExpr diff --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp index 0d7f3b3..722b24f 100644 --- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp +++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp @@ -11,15 +11,18 @@ // //===----------------------------------------------------------------------===// +#include #include #include +#include "clang/AST/DeclCXX.h" #include "clang/Analysis/Analyses/PostOrderCFGView.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "clang/Analysis/FlowSensitive/DataflowWorklist.h" #include "clang/Analysis/FlowSensitive/Transfer.h" #include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h" +#include "clang/Analysis/FlowSensitive/Value.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" #include "llvm/Support/raw_ostream.h" @@ -103,6 +106,60 @@ static TypeErasedDataflowAnalysisState computeBlockInputState( return *MaybeState; } +/// Transfers `State` by evaluating `CfgStmt` in the context of `Analysis`. +/// `HandleTransferredStmt` (if provided) will be applied to `CfgStmt`, after it +/// is evaluated. +static void +transferCFGStmt(const CFGStmt &CfgStmt, TypeErasedDataflowAnalysis &Analysis, + TypeErasedDataflowAnalysisState &State, + std::function + HandleTransferredStmt) { + const Stmt *S = CfgStmt.getStmt(); + assert(S != nullptr); + + transfer(*S, State.Env); + Analysis.transferTypeErased(S, State.Lattice, State.Env); + + if (HandleTransferredStmt != nullptr) + HandleTransferredStmt(CfgStmt, State); +} + +/// Transfers `State` by evaluating `CfgInit`. +static void transferCFGInitializer(const CFGInitializer &CfgInit, + TypeErasedDataflowAnalysisState &State) { + const auto &ThisLoc = *cast( + State.Env.getThisPointeeStorageLocation()); + + const CXXCtorInitializer *Initializer = CfgInit.getInitializer(); + assert(Initializer != nullptr); + + auto *InitStmt = Initializer->getInit(); + assert(InitStmt != nullptr); + + auto *InitStmtLoc = + State.Env.getStorageLocation(*InitStmt, SkipPast::Reference); + if (InitStmtLoc == nullptr) + return; + + auto *InitStmtVal = State.Env.getValue(*InitStmtLoc); + if (InitStmtVal == nullptr) + return; + + const FieldDecl *Member = Initializer->getMember(); + assert(Member != nullptr); + + if (Member->getType()->isReferenceType()) { + auto &MemberLoc = ThisLoc.getChild(*Member); + State.Env.setValue(MemberLoc, + State.Env.takeOwnership( + std::make_unique(*InitStmtLoc))); + } else { + auto &MemberLoc = ThisLoc.getChild(*Member); + State.Env.setValue(MemberLoc, *InitStmtVal); + } +} + TypeErasedDataflowAnalysisState transferBlock( const ControlFlowContext &CFCtx, std::vector> &BlockStates, @@ -114,19 +171,18 @@ TypeErasedDataflowAnalysisState transferBlock( TypeErasedDataflowAnalysisState State = computeBlockInputState(CFCtx, BlockStates, Block, InitEnv, Analysis); for (const CFGElement &Element : Block) { - // FIXME: Evaluate other kinds of `CFGElement`. - const llvm::Optional CfgStmt = Element.getAs(); - if (!CfgStmt.hasValue()) - continue; - - const Stmt *S = CfgStmt.getValue().getStmt(); - assert(S != nullptr); - - transfer(*S, State.Env); - Analysis.transferTypeErased(S, State.Lattice, State.Env); - - if (HandleTransferredStmt != nullptr) - HandleTransferredStmt(CfgStmt.getValue(), State); + switch (Element.getKind()) { + case CFGElement::Statement: + transferCFGStmt(*Element.getAs(), Analysis, State, + HandleTransferredStmt); + break; + case CFGElement::Initializer: + transferCFGInitializer(*Element.getAs(), State); + break; + default: + // FIXME: Evaluate other kinds of `CFGElement`. + break; + } } return State; } diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp index 55e15e2..1c5e49d 100644 --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -1171,4 +1171,114 @@ TEST_F(TransferTest, ClassThisMember) { }); } +TEST_F(TransferTest, ConstructorInitializer) { + std::string Code = R"( + struct target { + int Bar; + + target(int Foo) : Bar(Foo) { + int Qux = Bar; + // [[p]] + } + }; + )"; + runDataflow(Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const auto *ThisLoc = dyn_cast( + Env.getThisPointeeStorageLocation()); + ASSERT_THAT(ThisLoc, NotNull()); + + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); + + const auto *FooVal = + cast(Env.getValue(*FooDecl, SkipPast::None)); + + const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux"); + ASSERT_THAT(QuxDecl, NotNull()); + EXPECT_EQ(Env.getValue(*QuxDecl, SkipPast::None), FooVal); + }); +} + +TEST_F(TransferTest, DefaultInitializer) { + std::string Code = R"( + struct target { + int Bar; + int Baz = Bar; + + target(int Foo) : Bar(Foo) { + int Qux = Baz; + // [[p]] + } + }; + )"; + runDataflow(Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const auto *ThisLoc = dyn_cast( + Env.getThisPointeeStorageLocation()); + ASSERT_THAT(ThisLoc, NotNull()); + + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); + + const auto *FooVal = + cast(Env.getValue(*FooDecl, SkipPast::None)); + + const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux"); + ASSERT_THAT(QuxDecl, NotNull()); + EXPECT_EQ(Env.getValue(*QuxDecl, SkipPast::None), FooVal); + }); +} + +TEST_F(TransferTest, DefaultInitializerReference) { + std::string Code = R"( + struct target { + int &Bar; + int &Baz = Bar; + + target(int &Foo) : Bar(Foo) { + int &Qux = Baz; + // [[p]] + } + }; + )"; + runDataflow( + Code, [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const auto *ThisLoc = dyn_cast( + Env.getThisPointeeStorageLocation()); + ASSERT_THAT(ThisLoc, NotNull()); + + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); + + const auto *FooVal = + cast(Env.getValue(*FooDecl, SkipPast::None)); + + const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "Qux"); + ASSERT_THAT(QuxDecl, NotNull()); + + const auto *QuxVal = + cast(Env.getValue(*QuxDecl, SkipPast::None)); + EXPECT_EQ(&QuxVal->getPointeeLoc(), &FooVal->getPointeeLoc()); + }); +} + } // namespace -- 2.7.4