Vars.insert(V);
}
-static void getFieldsAndGlobalVars(const Decl &D,
- llvm::DenseSet<const FieldDecl *> &Fields,
- llvm::DenseSet<const VarDecl *> &Vars) {
+static void insertIfFunction(const Decl &D,
+ llvm::DenseSet<const FunctionDecl *> &Funcs) {
+ if (auto *FD = dyn_cast<FunctionDecl>(&D))
+ Funcs.insert(FD);
+}
+
+static void
+getFieldsGlobalsAndFuncs(const Decl &D,
+ llvm::DenseSet<const FieldDecl *> &Fields,
+ llvm::DenseSet<const VarDecl *> &Vars,
+ llvm::DenseSet<const FunctionDecl *> &Funcs) {
insertIfGlobal(D, Vars);
+ insertIfFunction(D, Funcs);
if (const auto *Decomp = dyn_cast<DecompositionDecl>(&D))
for (const auto *B : Decomp->bindings())
if (auto *ME = dyn_cast_or_null<MemberExpr>(B->getBinding()))
Fields.insert(FD);
}
-/// Traverses `S` and inserts into `Vars` any global storage values that are
-/// declared in or referenced from sub-statements.
-static void getFieldsAndGlobalVars(const Stmt &S,
- llvm::DenseSet<const FieldDecl *> &Fields,
- llvm::DenseSet<const VarDecl *> &Vars) {
+/// Traverses `S` and inserts into `Fields`, `Vars` and `Funcs` any fields,
+/// global variables and functions that are declared in or referenced from
+/// sub-statements.
+static void
+getFieldsGlobalsAndFuncs(const Stmt &S,
+ llvm::DenseSet<const FieldDecl *> &Fields,
+ llvm::DenseSet<const VarDecl *> &Vars,
+ llvm::DenseSet<const FunctionDecl *> &Funcs) {
for (auto *Child : S.children())
if (Child != nullptr)
- getFieldsAndGlobalVars(*Child, Fields, Vars);
+ getFieldsGlobalsAndFuncs(*Child, Fields, Vars, Funcs);
if (auto *DS = dyn_cast<DeclStmt>(&S)) {
if (DS->isSingleDecl())
- getFieldsAndGlobalVars(*DS->getSingleDecl(), Fields, Vars);
+ getFieldsGlobalsAndFuncs(*DS->getSingleDecl(), Fields, Vars, Funcs);
else
for (auto *D : DS->getDeclGroup())
- getFieldsAndGlobalVars(*D, Fields, Vars);
+ getFieldsGlobalsAndFuncs(*D, Fields, Vars, Funcs);
} else if (auto *E = dyn_cast<DeclRefExpr>(&S)) {
insertIfGlobal(*E->getDecl(), Vars);
+ insertIfFunction(*E->getDecl(), Funcs);
} else if (auto *E = dyn_cast<MemberExpr>(&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<FieldDecl>(VD))
Fields.insert(FD);
}
// FIXME: Add support for resetting globals after function calls to enable
// the implementation of sound analyses.
-void Environment::initFieldsAndGlobals(const FunctionDecl *FuncDecl) {
+void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) {
assert(FuncDecl->getBody() != nullptr);
llvm::DenseSet<const FieldDecl *> Fields;
llvm::DenseSet<const VarDecl *> Vars;
+ llvm::DenseSet<const FunctionDecl *> Funcs;
// Look for global variable and field references in the
// constructor-initializers.
Fields.insert(M);
const Expr *E = Init->getInit();
assert(E != nullptr);
- getFieldsAndGlobalVars(*E, Fields, Vars);
+ getFieldsGlobalsAndFuncs(*E, Fields, Vars, Funcs);
}
// Add all fields mentioned in default member initializers.
for (const FieldDecl *F : CtorDecl->getParent()->fields())
if (const auto *I = F->getInClassInitializer())
- getFieldsAndGlobalVars(*I, Fields, Vars);
+ getFieldsGlobalsAndFuncs(*I, Fields, Vars, Funcs);
}
- getFieldsAndGlobalVars(*FuncDecl->getBody(), Fields, Vars);
+ getFieldsGlobalsAndFuncs(*FuncDecl->getBody(), Fields, Vars, Funcs);
// These have to be added before the lines that follow to ensure that
// `create*` work correctly for structs.
if (auto *Val = createValue(D->getType()))
setValue(Loc, *Val);
}
+
+ for (const FunctionDecl *FD : Funcs) {
+ if (getStorageLocation(*FD, SkipPast::None) != nullptr)
+ continue;
+ auto &Loc = createStorageLocation(FD->getType());
+ setStorageLocation(*FD, Loc);
+ }
}
Environment::Environment(DataflowAnalysisContext &DACtx)
if (const auto *FuncDecl = dyn_cast<FunctionDecl>(&DeclCtx)) {
assert(FuncDecl->getBody() != nullptr);
- initFieldsAndGlobals(FuncDecl);
+ initFieldsGlobalsAndFuncs(FuncDecl);
for (const auto *ParamDecl : FuncDecl->parameters()) {
assert(ParamDecl != nullptr);
ArrayRef<const Expr *> Args) {
CallStack.push_back(FuncDecl);
- initFieldsAndGlobals(FuncDecl);
+ initFieldsGlobalsAndFuncs(FuncDecl);
const auto *ParamIt = FuncDecl->param_begin();
});
}
+TEST(TransferTest, FunctionToPointerDecayHasValue) {
+ std::string Code = R"(
+ struct A { static void static_member_func(); };
+ void target() {
+ // To check that we're treating function-to-pointer decay correctly,
+ // create two pointers, then verify they refer to the same storage
+ // location.
+ // We need to do the test this way because even if an initializer (in this
+ // case, the function-to-pointer decay) does not create a value, we still
+ // create a value for the variable.
+ void (*non_member_p1)() = target;
+ void (*non_member_p2)() = target;
+
+ // Do the same thing but for a static member function.
+ void (*member_p1)() = A::static_member_func;
+ void (*member_p2)() = A::static_member_func;
+ // [[p]]
+ }
+ )";
+ runDataflow(
+ Code,
+ [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
+ ASTContext &ASTCtx) {
+ const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
+
+ auto &NonMemberP1 =
+ getValueForDecl<PointerValue>(ASTCtx, Env, "non_member_p1");
+ auto &NonMemberP2 =
+ getValueForDecl<PointerValue>(ASTCtx, Env, "non_member_p2");
+ EXPECT_EQ(&NonMemberP1.getPointeeLoc(), &NonMemberP2.getPointeeLoc());
+
+ auto &MemberP1 =
+ getValueForDecl<PointerValue>(ASTCtx, Env, "member_p1");
+ auto &MemberP2 =
+ getValueForDecl<PointerValue>(ASTCtx, Env, "member_p2");
+ EXPECT_EQ(&MemberP1.getPointeeLoc(), &MemberP2.getPointeeLoc());
+ });
+}
+
} // namespace