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 @@ -199,6 +199,22 @@ assert(SubExpr != nullptr); switch (S->getCastKind()) { + case CK_IntegralToBoolean: { + // This cast creates a new, boolean value from the integral value. We + // model that with a fresh value in the environment, unless it's already a + // boolean. + auto &ExprLoc = Env.createStorageLocation(*S); + Env.setStorageLocation(*S, ExprLoc); + if (auto *SubExprVal = dyn_cast_or_null( + Env.getValue(*SubExpr, SkipPast::Reference))) + Env.setValue(ExprLoc, *SubExprVal); + else + // FIXME: If integer modeling is added, then update this code to create + // the boolean based on the integer model. + Env.setValue(ExprLoc, Env.makeAtomicBoolValue()); + break; + } + case CK_LValueToRValue: { auto *SubExprVal = Env.getValue(*SubExpr, SkipPast::Reference); if (SubExprVal == nullptr) @@ -209,6 +225,13 @@ Env.setValue(ExprLoc, *SubExprVal); break; } + + case CK_IntegralCast: + // FIXME: This cast creates a new integral value from the + // subexpression. But, because we don't model integers, we don't + // distinguish between this new value and the underlying one. If integer + // modeling is added, then update this code to create a fresh location and + // value. case CK_UncheckedDerivedToBase: case CK_ConstructorConversion: case CK_UserDefinedConversion: 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 @@ -1900,11 +1900,87 @@ const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); ASSERT_THAT(BarDecl, NotNull()); - const auto *FooVal = - cast(Env.getValue(*FooDecl, SkipPast::None)); - const auto *BarVal = - cast(Env.getValue(*BarDecl, SkipPast::None)); - EXPECT_EQ(FooVal, BarVal); + EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), + Env.getValue(*BarDecl, SkipPast::None)); + }); +} + +TEST_F(TransferTest, IntegralCast) { + std::string Code = R"( + void target(int Foo) { + long Bar = Foo; + // [[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 *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), + Env.getValue(*BarDecl, SkipPast::None)); + }); +} + +TEST_F(TransferTest, IntegraltoBooleanCast) { + std::string Code = R"( + void target(int Foo) { + bool Bar = Foo; + // [[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 *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + EXPECT_NE(Env.getValue(*FooDecl, SkipPast::None), + Env.getValue(*BarDecl, SkipPast::None)); + }); +} + +TEST_F(TransferTest, IntegraltoBooleanCastFromBool) { + std::string Code = R"( + void target(bool Foo) { + int Zab = Foo; + bool Bar = Zab; + // [[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 *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), + Env.getValue(*BarDecl, SkipPast::None)); }); } @@ -2394,6 +2470,35 @@ }); } +// `__builtin_expect` takes and returns a `long` argument, so other types +// involve casts. This verifies that we identify the input and output in that +// case. +TEST_F(TransferTest, BuiltinExpectBoolArg) { + std::string Code = R"( + void target(bool Foo) { + bool Bar = __builtin_expect(Foo, true); + /*[[p]]*/ + } + )"; + runDataflow(Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const auto &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()); + + EXPECT_EQ(Env.getValue(*FooDecl, SkipPast::None), + Env.getValue(*BarDecl, SkipPast::None)); + }); +} + TEST_F(TransferTest, BuiltinUnreachable) { std::string Code = R"( void target(bool Foo) {