Skip to content

Commit 40381d1

Browse files
authored
[clang][dataflow] Re-land: Retrieve members from accessors called usi… (llvm#74336)
…ng member pointers. This initially landed with a broken test due to a mid-air collision with a new requirement for Environment initialization before field modeling. Have added that initialization in the test. From first landing: getMethodDecl does not handle pointers to members and returns nullptr for them. getMethodDecl contains a decade-plus-old FIXME to handle pointers to members, but two approaches I looked at for fixing it are more invasive or complex than simply swapping to getCalleeDecl. The first, have getMethodDecl call getCalleeDecl, creates a large tree of const-ness mismatches due to getMethodDecl returning a non-const value while being a const member function and getCalleeDecl only being a const member function when it returns a const value. The second, implementing an AST walk to match how CXXMemberCallExpr::getImplicitObjectArgument grabs the LHS of the binary operator, is basically reimplementing Expr::getReferencedDeclOfCallee, which is used by Expr::getCalleeDecl. We don't need another copy of that code.
1 parent 72c6ca6 commit 40381d1

File tree

2 files changed

+57
-2
lines changed

2 files changed

+57
-2
lines changed

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,12 @@ static void insertIfFunction(const Decl &D,
300300
}
301301

302302
static MemberExpr *getMemberForAccessor(const CXXMemberCallExpr &C) {
303-
if (!C.getMethodDecl())
303+
// Use getCalleeDecl instead of getMethodDecl in order to handle
304+
// pointer-to-member calls.
305+
const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(C.getCalleeDecl());
306+
if (!MethodDecl)
304307
return nullptr;
305-
auto *Body = dyn_cast_or_null<CompoundStmt>(C.getMethodDecl()->getBody());
308+
auto *Body = dyn_cast_or_null<CompoundStmt>(MethodDecl->getBody());
306309
if (!Body || Body->size() != 1)
307310
return nullptr;
308311
if (auto *RS = dyn_cast<ReturnStmt>(*Body->body_begin()))

clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ namespace {
2525
using namespace clang;
2626
using namespace dataflow;
2727
using ::clang::dataflow::test::getFieldValue;
28+
using ::testing::Contains;
2829
using ::testing::IsNull;
2930
using ::testing::NotNull;
3031

@@ -311,6 +312,57 @@ TEST_F(EnvironmentTest, InitGlobalVarsConstructor) {
311312
EXPECT_THAT(Env.getValue(*Var), NotNull());
312313
}
313314

315+
// Pointers to Members are a tricky case of accessor calls, complicated further
316+
// when using templates where the pointer to the member is a template argument.
317+
// This is a repro of a failure case seen in the wild.
318+
TEST_F(EnvironmentTest,
319+
ModelMemberForAccessorUsingMethodPointerThroughTemplate) {
320+
using namespace ast_matchers;
321+
322+
std::string Code = R"cc(
323+
struct S {
324+
int accessor() {return member;}
325+
326+
int member = 0;
327+
};
328+
329+
template <auto method>
330+
int Target(S* S) {
331+
return (S->*method)();
332+
}
333+
334+
// We want to analyze the instantiation of Target for the accessor.
335+
int Instantiator () {S S; return Target<&S::accessor>(&S); }
336+
)cc";
337+
338+
auto Unit =
339+
// C++17 for the simplifying use of auto in the template declaration.
340+
tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
341+
auto &Context = Unit->getASTContext();
342+
343+
ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
344+
345+
auto Results = match(
346+
decl(anyOf(functionDecl(hasName("Target"), isTemplateInstantiation())
347+
.bind("target"),
348+
fieldDecl(hasName("member")).bind("member"),
349+
recordDecl(hasName("S")).bind("struct"))),
350+
Context);
351+
const auto *Fun = selectFirst<FunctionDecl>("target", Results);
352+
const auto *Struct = selectFirst<RecordDecl>("struct", Results);
353+
const auto *Member = selectFirst<FieldDecl>("member", Results);
354+
ASSERT_THAT(Fun, NotNull());
355+
ASSERT_THAT(Struct, NotNull());
356+
ASSERT_THAT(Member, NotNull());
357+
358+
// Verify that `member` is modeled for `S` when we analyze
359+
// `Target<&S::accessor>`.
360+
Environment Env(DAContext, *Fun);
361+
Env.initialize();
362+
EXPECT_THAT(DAContext.getModeledFields(QualType(Struct->getTypeForDecl(), 0)),
363+
Contains(Member));
364+
}
365+
314366
TEST_F(EnvironmentTest, RefreshRecordValue) {
315367
using namespace ast_matchers;
316368

0 commit comments

Comments
 (0)