llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Samira Bazuzi (bazuzi) <details> <summary>Changes</summary> Exposes the collection functionality, but does not alter it beyond using a return value instead of output parameters. Also relocates underlying and related functions and a class from DataflowEnvironment's files to DataflowAnalysisContext's files, as no Environment is needed. --- Patch is 21.33 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/88534.diff 5 Files Affected: - (modified) clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h (+46) - (modified) clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h (-36) - (modified) clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp (+174) - (modified) clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp (+4-172) - (modified) clang/lib/Analysis/FlowSensitive/Transfer.cpp (+1) ``````````diff diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h index 909a91059438ca..a34e5f603eb396 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h @@ -62,6 +62,52 @@ FieldSet getObjectFields(QualType Type); bool containsSameFields(const FieldSet &Fields, const RecordStorageLocation::FieldToLoc &FieldLocs); +/// Returns the fields of a `RecordDecl` that are initialized by an +/// `InitListExpr`, in the order in which they appear in +/// `InitListExpr::inits()`. +/// `Init->getType()` must be a record type. +std::vector<const FieldDecl *> +getFieldsForInitListExpr(const InitListExpr *InitList); + +/// Helper class for initialization of a record with an `InitListExpr`. +/// `InitListExpr::inits()` contains the initializers for both the base classes +/// and the fields of the record; this helper class separates these out into two +/// different lists. In addition, it deals with special cases associated with +/// unions. +class RecordInitListHelper { +public: + // `InitList` must have record type. + RecordInitListHelper(const InitListExpr *InitList); + + // Base classes with their associated initializer expressions. + ArrayRef<std::pair<const CXXBaseSpecifier *, Expr *>> base_inits() const { + return BaseInits; + } + + // Fields with their associated initializer expressions. + ArrayRef<std::pair<const FieldDecl *, Expr *>> field_inits() const { + return FieldInits; + } + +private: + SmallVector<std::pair<const CXXBaseSpecifier *, Expr *>> BaseInits; + SmallVector<std::pair<const FieldDecl *, Expr *>> FieldInits; + + // We potentially synthesize an `ImplicitValueInitExpr` for unions. It's a + // member variable because we store a pointer to it in `FieldInits`. + std::optional<ImplicitValueInitExpr> ImplicitValueInitForUnion; +}; + +struct FieldsGlobalsAndFuncs { + FieldSet Fields; + // Globals includes all variables with global storage, notably including + // static data members and static variables declared within a function. + llvm::DenseSet<const VarDecl *> Globals; + llvm::DenseSet<const FunctionDecl *> Funcs; +}; + +FieldsGlobalsAndFuncs getFieldsGlobalsAndFuncs(const FunctionDecl &FD); + struct ContextSensitiveOptions { /// The maximum depth to analyze. A value of zero is equivalent to disabling /// context-sensitive analysis entirely. diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h index 706664d7db1c25..4277792219c0af 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -775,42 +775,6 @@ RecordStorageLocation *getImplicitObjectLocation(const CXXMemberCallExpr &MCE, RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME, const Environment &Env); -/// Returns the fields of a `RecordDecl` that are initialized by an -/// `InitListExpr`, in the order in which they appear in -/// `InitListExpr::inits()`. -/// `Init->getType()` must be a record type. -std::vector<const FieldDecl *> -getFieldsForInitListExpr(const InitListExpr *InitList); - -/// Helper class for initialization of a record with an `InitListExpr`. -/// `InitListExpr::inits()` contains the initializers for both the base classes -/// and the fields of the record; this helper class separates these out into two -/// different lists. In addition, it deals with special cases associated with -/// unions. -class RecordInitListHelper { -public: - // `InitList` must have record type. - RecordInitListHelper(const InitListExpr *InitList); - - // Base classes with their associated initializer expressions. - ArrayRef<std::pair<const CXXBaseSpecifier *, Expr *>> base_inits() const { - return BaseInits; - } - - // Fields with their associated initializer expressions. - ArrayRef<std::pair<const FieldDecl *, Expr *>> field_inits() const { - return FieldInits; - } - -private: - SmallVector<std::pair<const CXXBaseSpecifier *, Expr *>> BaseInits; - SmallVector<std::pair<const FieldDecl *, Expr *>> FieldInits; - - // We potentially synthesize an `ImplicitValueInitExpr` for unions. It's a - // member variable because we store a pointer to it in `FieldInits`. - std::optional<ImplicitValueInitExpr> ImplicitValueInitForUnion; -}; - /// Associates a new `RecordValue` with `Loc` and returns the new value. RecordValue &refreshRecordValue(RecordStorageLocation &Loc, Environment &Env); diff --git a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp index d520539dd25355..74b1299f014c66 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp @@ -55,6 +55,180 @@ FieldSet DataflowAnalysisContext::getModeledFields(QualType Type) { return llvm::set_intersection(getObjectFields(Type), ModeledFields); } +std::vector<const FieldDecl *> +getFieldsForInitListExpr(const InitListExpr *InitList) { + const RecordDecl *RD = InitList->getType()->getAsRecordDecl(); + assert(RD != nullptr); + + std::vector<const FieldDecl *> Fields; + + if (InitList->getType()->isUnionType()) { + Fields.push_back(InitList->getInitializedFieldInUnion()); + return Fields; + } + + // Unnamed bitfields are only used for padding and do not appear in + // `InitListExpr`'s inits. However, those fields do appear in `RecordDecl`'s + // field list, and we thus need to remove them before mapping inits to + // fields to avoid mapping inits to the wrongs fields. + llvm::copy_if( + RD->fields(), std::back_inserter(Fields), + [](const FieldDecl *Field) { return !Field->isUnnamedBitfield(); }); + return Fields; +} + +RecordInitListHelper::RecordInitListHelper(const InitListExpr *InitList) { + auto *RD = InitList->getType()->getAsCXXRecordDecl(); + assert(RD != nullptr); + + std::vector<const FieldDecl *> Fields = getFieldsForInitListExpr(InitList); + ArrayRef<Expr *> Inits = InitList->inits(); + + // Unions initialized with an empty initializer list need special treatment. + // For structs/classes initialized with an empty initializer list, Clang + // puts `ImplicitValueInitExpr`s in `InitListExpr::inits()`, but for unions, + // it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves. + SmallVector<Expr *> InitsForUnion; + if (InitList->getType()->isUnionType() && Inits.empty()) { + assert(Fields.size() == 1); + ImplicitValueInitForUnion.emplace(Fields.front()->getType()); + InitsForUnion.push_back(&*ImplicitValueInitForUnion); + Inits = InitsForUnion; + } + + size_t InitIdx = 0; + + assert(Fields.size() + RD->getNumBases() == Inits.size()); + for (const CXXBaseSpecifier &Base : RD->bases()) { + assert(InitIdx < Inits.size()); + Expr *Init = Inits[InitIdx++]; + BaseInits.emplace_back(&Base, Init); + } + + assert(Fields.size() == Inits.size() - InitIdx); + for (const FieldDecl *Field : Fields) { + assert(InitIdx < Inits.size()); + Expr *Init = Inits[InitIdx++]; + FieldInits.emplace_back(Field, Init); + } +} + +static void insertIfGlobal(const Decl &D, + llvm::DenseSet<const VarDecl *> &Vars) { + if (auto *V = dyn_cast<VarDecl>(&D)) + if (V->hasGlobalStorage()) + Vars.insert(V); +} + +static void insertIfFunction(const Decl &D, + llvm::DenseSet<const FunctionDecl *> &Funcs) { + if (auto *FD = dyn_cast<FunctionDecl>(&D)) + Funcs.insert(FD); +} + +static MemberExpr *getMemberForAccessor(const CXXMemberCallExpr &C) { + // Use getCalleeDecl instead of getMethodDecl in order to handle + // pointer-to-member calls. + const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(C.getCalleeDecl()); + if (!MethodDecl) + return nullptr; + auto *Body = dyn_cast_or_null<CompoundStmt>(MethodDecl->getBody()); + if (!Body || Body->size() != 1) + return nullptr; + if (auto *RS = dyn_cast<ReturnStmt>(*Body->body_begin())) + if (auto *Return = RS->getRetValue()) + return dyn_cast<MemberExpr>(Return->IgnoreParenImpCasts()); + return nullptr; +} + +static void +getFieldsGlobalsAndFuncs(const Decl &D, FieldSet &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())) + // FIXME: should we be using `E->getFoundDecl()`? + if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl())) + Fields.insert(FD); +} + +/// 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, FieldSet &Fields, + llvm::DenseSet<const VarDecl *> &Vars, + llvm::DenseSet<const FunctionDecl *> &Funcs) { + for (auto *Child : S.children()) + if (Child != nullptr) + getFieldsGlobalsAndFuncs(*Child, Fields, Vars, Funcs); + if (const auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(&S)) + getFieldsGlobalsAndFuncs(*DefaultArg->getExpr(), Fields, Vars, Funcs); + if (const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(&S)) + getFieldsGlobalsAndFuncs(*DefaultInit->getExpr(), Fields, Vars, Funcs); + + if (auto *DS = dyn_cast<DeclStmt>(&S)) { + if (DS->isSingleDecl()) + getFieldsGlobalsAndFuncs(*DS->getSingleDecl(), Fields, Vars, Funcs); + else + for (auto *D : DS->getDeclGroup()) + getFieldsGlobalsAndFuncs(*D, Fields, Vars, Funcs); + } else if (auto *E = dyn_cast<DeclRefExpr>(&S)) { + insertIfGlobal(*E->getDecl(), Vars); + insertIfFunction(*E->getDecl(), Funcs); + } else if (const auto *C = dyn_cast<CXXMemberCallExpr>(&S)) { + // If this is a method that returns a member variable but does nothing else, + // model the field of the return value. + if (MemberExpr *E = getMemberForAccessor(*C)) + if (const auto *FD = dyn_cast<FieldDecl>(E->getMemberDecl())) + Fields.insert(FD); + } 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); + } else if (auto *InitList = dyn_cast<InitListExpr>(&S)) { + if (InitList->getType()->isRecordType()) + for (const auto *FD : getFieldsForInitListExpr(InitList)) + Fields.insert(FD); + } +} + +/// Collects and returns fields, global variables and functions that are +/// declared in or referenced from `FD`. +FieldsGlobalsAndFuncs getFieldsGlobalsAndFuncs(const FunctionDecl &FD) { + FieldsGlobalsAndFuncs Result; + // Look for global variable and field references in the + // constructor-initializers. + if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(&FD)) { + for (const auto *Init : CtorDecl->inits()) { + if (Init->isMemberInitializer()) { + Result.Fields.insert(Init->getMember()); + } else if (Init->isIndirectMemberInitializer()) { + for (const auto *I : Init->getIndirectMember()->chain()) + Result.Fields.insert(cast<FieldDecl>(I)); + } + const Expr *E = Init->getInit(); + assert(E != nullptr); + getFieldsGlobalsAndFuncs(*E, Result.Fields, Result.Globals, Result.Funcs); + } + // Add all fields mentioned in default member initializers. + for (const FieldDecl *F : CtorDecl->getParent()->fields()) + if (const auto *I = F->getInClassInitializer()) + getFieldsGlobalsAndFuncs(*I, Result.Fields, Result.Globals, + Result.Funcs); + } + getFieldsGlobalsAndFuncs(*FD.getBody(), Result.Fields, Result.Globals, + Result.Funcs); + + return Result; +} + void DataflowAnalysisContext::addModeledFields(const FieldSet &Fields) { ModeledFields.set_union(Fields); } diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp index bea15ce9bd24d1..6dae25441968de 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -304,93 +304,6 @@ widenKeyToValueMap(const llvm::MapVector<Key, Value *> &CurMap, return WidenedMap; } -/// Initializes a global storage value. -static void insertIfGlobal(const Decl &D, - llvm::DenseSet<const VarDecl *> &Vars) { - if (auto *V = dyn_cast<VarDecl>(&D)) - if (V->hasGlobalStorage()) - Vars.insert(V); -} - -static void insertIfFunction(const Decl &D, - llvm::DenseSet<const FunctionDecl *> &Funcs) { - if (auto *FD = dyn_cast<FunctionDecl>(&D)) - Funcs.insert(FD); -} - -static MemberExpr *getMemberForAccessor(const CXXMemberCallExpr &C) { - // Use getCalleeDecl instead of getMethodDecl in order to handle - // pointer-to-member calls. - const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(C.getCalleeDecl()); - if (!MethodDecl) - return nullptr; - auto *Body = dyn_cast_or_null<CompoundStmt>(MethodDecl->getBody()); - if (!Body || Body->size() != 1) - return nullptr; - if (auto *RS = dyn_cast<ReturnStmt>(*Body->body_begin())) - if (auto *Return = RS->getRetValue()) - return dyn_cast<MemberExpr>(Return->IgnoreParenImpCasts()); - return nullptr; -} - -static void -getFieldsGlobalsAndFuncs(const Decl &D, FieldSet &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())) - // FIXME: should we be using `E->getFoundDecl()`? - if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl())) - Fields.insert(FD); -} - -/// 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, FieldSet &Fields, - llvm::DenseSet<const VarDecl *> &Vars, - llvm::DenseSet<const FunctionDecl *> &Funcs) { - for (auto *Child : S.children()) - if (Child != nullptr) - getFieldsGlobalsAndFuncs(*Child, Fields, Vars, Funcs); - if (const auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(&S)) - getFieldsGlobalsAndFuncs(*DefaultArg->getExpr(), Fields, Vars, Funcs); - if (const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(&S)) - getFieldsGlobalsAndFuncs(*DefaultInit->getExpr(), Fields, Vars, Funcs); - - if (auto *DS = dyn_cast<DeclStmt>(&S)) { - if (DS->isSingleDecl()) - getFieldsGlobalsAndFuncs(*DS->getSingleDecl(), Fields, Vars, Funcs); - else - for (auto *D : DS->getDeclGroup()) - getFieldsGlobalsAndFuncs(*D, Fields, Vars, Funcs); - } else if (auto *E = dyn_cast<DeclRefExpr>(&S)) { - insertIfGlobal(*E->getDecl(), Vars); - insertIfFunction(*E->getDecl(), Funcs); - } else if (const auto *C = dyn_cast<CXXMemberCallExpr>(&S)) { - // If this is a method that returns a member variable but does nothing else, - // model the field of the return value. - if (MemberExpr *E = getMemberForAccessor(*C)) - if (const auto *FD = dyn_cast<FieldDecl>(E->getMemberDecl())) - Fields.insert(FD); - } 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); - } else if (auto *InitList = dyn_cast<InitListExpr>(&S)) { - if (InitList->getType()->isRecordType()) - for (const auto *FD : getFieldsForInitListExpr(InitList)) - Fields.insert(FD); - } -} - namespace { // Visitor that builds a map from record prvalues to result objects. @@ -648,36 +561,13 @@ void Environment::initialize() { void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) { assert(FuncDecl->doesThisDeclarationHaveABody()); - FieldSet Fields; - llvm::DenseSet<const VarDecl *> Vars; - llvm::DenseSet<const FunctionDecl *> Funcs; - - // Look for global variable and field references in the - // constructor-initializers. - if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FuncDecl)) { - for (const auto *Init : CtorDecl->inits()) { - if (Init->isMemberInitializer()) { - Fields.insert(Init->getMember()); - } else if (Init->isIndirectMemberInitializer()) { - for (const auto *I : Init->getIndirectMember()->chain()) - Fields.insert(cast<FieldDecl>(I)); - } - const Expr *E = Init->getInit(); - assert(E != nullptr); - 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()) - getFieldsGlobalsAndFuncs(*I, Fields, Vars, Funcs); - } - getFieldsGlobalsAndFuncs(*FuncDecl->getBody(), Fields, Vars, Funcs); + FieldsGlobalsAndFuncs FGF = getFieldsGlobalsAndFuncs(*FuncDecl); // These have to be added before the lines that follow to ensure that // `create*` work correctly for structs. - DACtx->addModeledFields(Fields); + DACtx->addModeledFields(FGF.Fields); - for (const VarDecl *D : Vars) { + for (const VarDecl *D : FGF.Globals) { if (getStorageLocation(*D) != nullptr) continue; @@ -689,7 +579,7 @@ void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) { setStorageLocation(*D, createObject(*D, nullptr)); } - for (const FunctionDecl *FD : Funcs) { + for (const FunctionDecl *FD : FGF.Funcs) { if (getStorageLocation(*FD) != nullptr) continue; auto &Loc = createStorageLocation(*FD); @@ -1349,64 +1239,6 @@ RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME, return Env.get<RecordStorageLocation>(*Base); } -std::vector<const FieldDecl *> -getFieldsForInitListExpr(const InitListExpr *InitList) { - const RecordDecl *RD = InitList->getType()->getAsRecordDecl(); - assert(RD != nullptr); - - std::vector<const FieldDecl *> Fields; - - if (InitList->getType()->isUnionType()) { - Fields.push_back(InitList->getInitializedFieldInUnion()); - return Fields; - } - - // Unnamed bitfields are only used for padding and do not appear in - // `InitListExpr`'s inits. However, those fields do appear in `RecordDecl`'s - // field list, and we thus need to remove them before mapping inits to - // fields to avoid mapping inits to the wrongs fields. - llvm::copy_if( - RD->fields(), std::back_inserter(Fields), - [](const FieldDecl *Field) { return !Field->isUnnamedBitfield(); }); - return Fields; -} - -RecordInitListHelper::RecordInitListHelper(const InitListExpr *InitList) { - auto *RD = InitList->getType()->getAsCXXRecordDecl(); - assert(RD != nullptr); - - std::vector<const FieldDecl *> Fields = getFieldsForInitListExpr(InitList); - ArrayRef<Expr *> Inits = InitList->inits(); - - // Unions initialized with an empty initializer list need special treatment. - // For structs/classes initialized with an empty initializer list, Clang - // puts `ImplicitValueInitExpr`s in `InitListExpr::inits()`, but for unions, - // it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves. - SmallVector<Expr *> InitsForUnion; - if (InitList->getType()->isUnionType() && Inits.empty()) { - assert(Fields.size() == 1); - ImplicitValueInitForUnion.emplace(Fields.front()->getType()); - Init... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/88534 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits