Skip to content

[clang][dataflow] Strengthen pointer comparison. #75170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions clang/lib/Analysis/FlowSensitive/Transfer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,14 @@ static BoolValue &evaluateBooleanEquality(const Expr &LHS, const Expr &RHS,
if (auto *RHSBool = dyn_cast_or_null<BoolValue>(RHSValue))
return Env.makeIff(*LHSBool, *RHSBool);

if (auto *LHSPtr = dyn_cast_or_null<PointerValue>(LHSValue))
if (auto *RHSPtr = dyn_cast_or_null<PointerValue>(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();
}

Expand Down
88 changes: 88 additions & 0 deletions clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4586,6 +4586,94 @@ TEST(TransferTest, BooleanInequality) {
});
}

TEST(TransferTest, PointerEquality) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be great to add a test case with union members to document the limitation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point -- done.

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<DataflowAnalysisState<NoopLattice>> &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<PointerValue>(ASTCtx, Env, "p1"),
&getValueForDecl<PointerValue>(ASTCtx, Env, "p2"));

EXPECT_EQ(&getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_p1"),
&Env.getBoolLiteralValue(true));
EXPECT_EQ(&getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_p2"),
&Env.getBoolLiteralValue(true));
EXPECT_TRUE(isa<AtomicBoolValue>(
getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_p_other")));

EXPECT_TRUE(isa<AtomicBoolValue>(
getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_null")));
EXPECT_TRUE(isa<AtomicBoolValue>(
getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_nullptr")));
EXPECT_EQ(&getValueForDecl<BoolValue>(ASTCtx, Env, "null_eq_nullptr"),
&Env.getBoolLiteralValue(true));
EXPECT_EQ(
&getValueForDecl<BoolValue>(ASTCtx, Env, "nullptr_eq_nullptr"),
&Env.getBoolLiteralValue(true));

EXPECT_EQ(&getValueForDecl<BoolValue>(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<DataflowAnalysisState<NoopLattice>> &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<AtomicBoolValue>(
getValueForDecl<BoolValue>(ASTCtx, Env, "i1_eq_i2")));
});
}

TEST(TransferTest, IntegerLiteralEquality) {
std::string Code = R"(
void target() {
Expand Down
Loading