diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -168,6 +168,12 @@ Funcs.insert(FD); } +static void insertIfField(const Decl &D, + llvm::DenseSet &Fields) { + if (const auto *FD = dyn_cast(&D)) + Fields.insert(FD); +} + static void getFieldsGlobalsAndFuncs(const Decl &D, llvm::DenseSet &Fields, @@ -204,13 +210,13 @@ } else if (auto *E = dyn_cast(&S)) { insertIfGlobal(*E->getDecl(), Vars); insertIfFunction(*E->getDecl(), Funcs); + insertIfField(*E->getDecl(), Fields); } else if (auto *E = dyn_cast(&S)) { // FIXME: should we be using `E->getFoundDecl()`? const ValueDecl *VD = E->getMemberDecl(); insertIfGlobal(*VD, Vars); insertIfFunction(*VD, Funcs); - if (const auto *FD = dyn_cast(VD)) - Fields.insert(FD); + insertIfField(*VD, Fields); } } @@ -259,6 +265,14 @@ auto &Loc = createStorageLocation(FD->getType()); setStorageLocation(*FD, Loc); } + + // Storage locations for fields are used in pointers to member variables. + for (const FieldDecl *Field : Fields) { + if (getStorageLocation(*Field) != nullptr) + continue; + auto &Loc = createStorageLocation(Field->getType()); + setStorageLocation(*Field, Loc); + } } Environment::Environment(DataflowAnalysisContext &DACtx) 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 @@ -231,6 +231,15 @@ void VisitDeclRefExpr(const DeclRefExpr *S) { const ValueDecl *VD = S->getDecl(); assert(VD != nullptr); + + // `DeclRefExpr`s to fields and non-static methods aren't glvalues, and + // there's also no sensible `Value` we can assign to them, so skip them. + if (isa(VD)) + return; + if (auto *Method = dyn_cast(VD); + Method && !Method->isStatic()) + return; + auto *DeclLoc = Env.getStorageLocation(*VD); if (DeclLoc == nullptr) return; @@ -440,14 +449,18 @@ break; } case UO_AddrOf: { - // Do not form a pointer to a reference. If `SubExpr` is assigned a - // `ReferenceValue` then form a value that points to the location of its - // pointee. - StorageLocation *PointeeLoc = Env.getStorageLocationStrict(*SubExpr); - if (PointeeLoc == nullptr) - break; + StorageLocation *PointeeLoc = nullptr; + if (S->getType()->isMemberPointerType()) { + if (auto *DeclRef = dyn_cast(SubExpr)) + if (auto *D = DeclRef->getDecl()) + PointeeLoc = Env.getStorageLocation(*D); + } else { + PointeeLoc = Env.getStorageLocationStrict(*SubExpr); + } + + if (PointeeLoc) + Env.setValueStrict(*S, Env.create(*PointeeLoc)); - Env.setValueStrict(*S, Env.create(*PointeeLoc)); break; } case UO_LNot: { 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 @@ -2523,6 +2523,54 @@ }); } +TEST(TransferTest, PointerToMemberVariable) { + std::string Code = R"( + struct S { + int i; + }; + void target() { + int S::*P1 = &S::i; + int S::*P2 = &S::i; + // [[p]] + } + )"; + runDataflow( + Code, + [](const llvm::StringMap> &Results, + ASTContext &ASTCtx) { + const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); + + auto &P1Val = getValueForDecl(ASTCtx, Env, "P1"); + auto &P2Val = getValueForDecl(ASTCtx, Env, "P2"); + + ASSERT_EQ(&P1Val.getPointeeLoc(), &P2Val.getPointeeLoc()); + }); +} + +TEST(TransferTest, PointerToMemberFunction) { + std::string Code = R"( + struct S { + void Method(); + }; + void target() { + void (S::*P1)() = &S::Method; + void (S::*P2)() = &S::Method; + // [[p]] + } + )"; + runDataflow( + Code, + [](const llvm::StringMap> &Results, + ASTContext &ASTCtx) { + const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); + + auto &P1Val = getValueForDecl(ASTCtx, Env, "P1"); + auto &P2Val = getValueForDecl(ASTCtx, Env, "P2"); + + ASSERT_EQ(&P1Val.getPointeeLoc(), &P2Val.getPointeeLoc()); + }); +} + TEST(TransferTest, NullToMemberPointerCast) { std::string Code = R"( struct Foo {};