diff --git a/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp b/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp --- a/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp +++ b/clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp @@ -49,6 +49,7 @@ 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 --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -164,6 +164,23 @@ } } + 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 --- 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 @@ 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 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 --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -1171,4 +1171,114 @@ }); } +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