Skip to content

Commit

Permalink
[clang][dataflow] Include fields initialized in an InitListExpr in …
Browse files Browse the repository at this point in the history
…`getModeledFields()`.

Previously, we were including these fields only in the specific instance that was initialized by the `InitListExpr`, but not in other instances of the same type. This is inconsistent and error-prone.

Depends On D154952

Reviewed By: xazax.hun, gribozavr2

Differential Revision: https://reviews.llvm.org/D154961
  • Loading branch information
martinboehme committed Jul 12, 2023
1 parent 1e9b4fc commit b47bdcb
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,10 @@ getImplicitObjectLocation(const CXXMemberCallExpr &MCE, const Environment &Env);
AggregateStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
const Environment &Env);

/// Returns the fields of `RD` that are initialized by an `InitListExpr`, in the
/// order in which they appear in `InitListExpr::inits()`.
std::vector<FieldDecl *> getFieldsForInitListExpr(const RecordDecl *RD);

} // namespace dataflow
} // namespace clang

Expand Down
16 changes: 16 additions & 0 deletions clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ getFieldsGlobalsAndFuncs(const Stmt &S, FieldSet &Fields,
insertIfFunction(*VD, Funcs);
if (const auto *FD = dyn_cast<FieldDecl>(VD))
Fields.insert(FD);
} else if (auto *InitList = dyn_cast<InitListExpr>(&S)) {
if (RecordDecl *RD = InitList->getType()->getAsRecordDecl())
for (const auto *FD : getFieldsForInitListExpr(RD))
Fields.insert(FD);
}
}

Expand Down Expand Up @@ -958,5 +962,17 @@ AggregateStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
return cast<AggregateStorageLocation>(Loc);
}

std::vector<FieldDecl *> getFieldsForInitListExpr(const RecordDecl *RD) {
// 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.
std::vector<FieldDecl *> Fields;
llvm::copy_if(
RD->fields(), std::back_inserter(Fields),
[](const FieldDecl *Field) { return !Field->isUnnamedBitfield(); });
return Fields;
}

} // namespace dataflow
} // namespace clang
10 changes: 2 additions & 8 deletions clang/lib/Analysis/FlowSensitive/Transfer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -715,14 +715,8 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
Env.setValue(Loc, *Val);

if (Type->isStructureOrClassType()) {
// Unnamed bitfields are only used for padding and are not appearing 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.
std::vector<FieldDecl *> Fields;
llvm::copy_if(
Type->getAsRecordDecl()->fields(), std::back_inserter(Fields),
[](const FieldDecl *Field) { return !Field->isUnnamedBitfield(); });
std::vector<FieldDecl *> Fields =
getFieldsForInitListExpr(Type->getAsRecordDecl());
for (auto It : llvm::zip(Fields, S->inits())) {
const FieldDecl *Field = std::get<0>(It);
assert(Field != nullptr);
Expand Down
10 changes: 10 additions & 0 deletions clang/unittests/Analysis/FlowSensitive/TransferTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2833,6 +2833,7 @@ TEST(TransferTest, AggregateInitialization) {
void target(int BarArg, int FooArg, int QuxArg) {
B Quux{BarArg, {FooArg}, QuxArg};
B OtherB;
/*[[p]]*/
}
)";
Expand All @@ -2849,6 +2850,7 @@ TEST(TransferTest, AggregateInitialization) {
void target(int BarArg, int FooArg, int QuxArg) {
B Quux = {BarArg, FooArg, QuxArg};
B OtherB;
/*[[p]]*/
}
)";
Expand Down Expand Up @@ -2898,6 +2900,14 @@ TEST(TransferTest, AggregateInitialization) {
EXPECT_EQ(getFieldValue(QuuxVal, *BarDecl, Env), BarArgVal);
EXPECT_EQ(getFieldValue(BazVal, *FooDecl, Env), FooArgVal);
EXPECT_EQ(getFieldValue(QuuxVal, *QuxDecl, Env), QuxArgVal);

// Check that fields initialized in an initializer list are always
// modeled in other instances of the same type.
const auto &OtherBVal =
getValueForDecl<StructValue>(ASTCtx, Env, "OtherB");
EXPECT_THAT(OtherBVal.getChild(*BarDecl), NotNull());
EXPECT_THAT(OtherBVal.getChild(*BazDecl), NotNull());
EXPECT_THAT(OtherBVal.getChild(*QuxDecl), NotNull());
});
}
}
Expand Down

0 comments on commit b47bdcb

Please sign in to comment.