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 @@ -32,6 +32,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" namespace clang { namespace dataflow { @@ -108,8 +109,32 @@ auto *Val = cast_or_null(Env.getValue(Cond, SkipPast::Reference)); - if (Val == nullptr) - return; + // Value merging depends on flow conditions from different environments + // being mutually exclusive. So, we need *some* value for the condition + // expression, even if just an atom. + if (Val == nullptr) { + Val = &Env.makeAtomicBoolValue(); + QualType Type = Cond.getType(); + assert(Type->isBooleanType() || Type->isReferenceType()); + if (Type->isBooleanType()) { + auto &Loc = Env.createStorageLocation(Cond); + Env.setStorageLocation(Cond, Loc); + Env.setValue(Loc, *Val); + } else if (auto *CondVal = cast_or_null( + Env.getValue(Cond, SkipPast::None))) { + Env.setValue(CondVal->getPointeeLoc(), *Val); + } else { + QualType PointeeType = Type->castAs()->getPointeeType(); + StorageLocation &PointeeLoc = Env.createStorageLocation(PointeeType); + Env.setValue(PointeeLoc, *Val); + auto *Loc = Env.getStorageLocation(Cond, SkipPast::None); + // `Cond` must have been processed already (and so must have an + // associated location) since it came from the predecessor block. + assert(Loc != nullptr); + Env.setValue(*Loc, Env.takeOwnership( + std::make_unique(PointeeLoc))); + } + } // The condition must be inverted for the successor that encompasses the // "else" branch, if such exists. 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 @@ -2890,6 +2890,68 @@ }); } +TEST_F(TransferTest, OpaqueFlowConditionMergesToOpaqueBool) { + std::string Code = R"( + bool foo(); + + void target() { + bool Bar = true; + if (foo()) + Bar = false; + (void)0; + /*[[p]]*/ + } + )"; + runDataflow( + Code, [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + auto &BarVal = + *cast(Env.getValue(*BarDecl, SkipPast::Reference)); + + EXPECT_FALSE(Env.flowConditionImplies(BarVal)); + }); +} + +TEST_F(TransferTest, OpaqueFlowConditionInsideBranchMergesToOpaqueBool) { + std::string Code = R"( + bool foo(); + + void target(bool Cond) { + bool Bar = true; + if (Cond) { + if (foo()) + Bar = false; + (void)0; + /*[[p]]*/ + } + } + )"; + runDataflow( + Code, [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + auto &BarVal = + *cast(Env.getValue(*BarDecl, SkipPast::Reference)); + + EXPECT_FALSE(Env.flowConditionImplies(BarVal)); + }); +} + TEST_F(TransferTest, CorrelatedBranches) { std::string Code = R"( void target(bool B, bool C) {