diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp index fd224aeb79b151..0ff0ca74ffd033 100644 --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -68,6 +68,14 @@ static BoolValue &evaluateBooleanEquality(const Expr &LHS, const Expr &RHS, if (auto *RHSBool = dyn_cast_or_null(RHSValue)) return Env.makeIff(*LHSBool, *RHSBool); + if (auto *LHSPtr = dyn_cast_or_null(LHSValue)) + if (auto *RHSPtr = dyn_cast_or_null(RHSValue)) + // If the storage locations are the same, the pointers definitely compare + // the same. If the storage locations are different, they may still alias, + // so we fall through to the case below that returns an atom. + if (&LHSPtr->getPointeeLoc() == &RHSPtr->getPointeeLoc()) + return Env.getBoolLiteralValue(true); + return Env.makeAtomicBoolValue(); } diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp index 301bec32c0cf1d..555f3f2024f8b8 100644 --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -4586,6 +4586,94 @@ TEST(TransferTest, BooleanInequality) { }); } +TEST(TransferTest, PointerEquality) { + std::string Code = R"cc( + void target() { + int i = 0; + int i_other = 0; + int *p1 = &i; + int *p2 = &i; + int *p_other = &i_other; + int *null = nullptr; + + bool p1_eq_p1 = (p1 == p1); + bool p1_eq_p2 = (p1 == p2); + bool p1_eq_p_other = (p1 == p_other); + + bool p1_eq_null = (p1 == null); + bool p1_eq_nullptr = (p1 == nullptr); + bool null_eq_nullptr = (null == nullptr); + bool nullptr_eq_nullptr = (nullptr == nullptr); + + // We won't duplicate all of the tests above with `!=`, as we know that + // the implementation simply negates the result of the `==` comparison. + // Instaed, just spot-check one case. + bool p1_ne_p1 = (p1 != p1); + + (void)0; // [[p]] + } + )cc"; + runDataflow( + Code, + [](const llvm::StringMap> &Results, + ASTContext &ASTCtx) { + const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); + + // Check the we have indeed set things up so that `p1` and `p2` have + // different pointer values. + EXPECT_NE(&getValueForDecl(ASTCtx, Env, "p1"), + &getValueForDecl(ASTCtx, Env, "p2")); + + EXPECT_EQ(&getValueForDecl(ASTCtx, Env, "p1_eq_p1"), + &Env.getBoolLiteralValue(true)); + EXPECT_EQ(&getValueForDecl(ASTCtx, Env, "p1_eq_p2"), + &Env.getBoolLiteralValue(true)); + EXPECT_TRUE(isa( + getValueForDecl(ASTCtx, Env, "p1_eq_p_other"))); + + EXPECT_TRUE(isa( + getValueForDecl(ASTCtx, Env, "p1_eq_null"))); + EXPECT_TRUE(isa( + getValueForDecl(ASTCtx, Env, "p1_eq_nullptr"))); + EXPECT_EQ(&getValueForDecl(ASTCtx, Env, "null_eq_nullptr"), + &Env.getBoolLiteralValue(true)); + EXPECT_EQ( + &getValueForDecl(ASTCtx, Env, "nullptr_eq_nullptr"), + &Env.getBoolLiteralValue(true)); + + EXPECT_EQ(&getValueForDecl(ASTCtx, Env, "p1_ne_p1"), + &Env.getBoolLiteralValue(false)); + }); +} + +TEST(TransferTest, PointerEqualityUnionMembers) { + std::string Code = R"cc( + union U { + int i1; + int i2; + }; + void target() { + U u; + bool i1_eq_i2 = (&u.i1 == &u.i2); + + (void)0; // [[p]] + } + )cc"; + runDataflow( + Code, + [](const llvm::StringMap> &Results, + ASTContext &ASTCtx) { + const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); + + // FIXME: By the standard, `u.i1` and `u.i2` should have the same + // address, but we don't yet model this property of union members + // correctly. The result is therefore weaker than it could be (just an + // atom rather than a true literal), though not wrong. + EXPECT_TRUE(isa( + getValueForDecl(ASTCtx, Env, "i1_eq_i2"))); + }); +} + TEST(TransferTest, IntegerLiteralEquality) { std::string Code = R"( void target() {