Skip to content

Commit ca7d944

Browse files
authored
[clang][dataflow] Support CXXParenListInitExpr in PropagateResultObject(). (#89235)
1 parent affcaf6 commit ca7d944

File tree

4 files changed

+131
-29
lines changed

4 files changed

+131
-29
lines changed

clang/include/clang/Analysis/FlowSensitive/ASTOps.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class RecordInitListHelper {
5656
public:
5757
// `InitList` must have record type.
5858
RecordInitListHelper(const InitListExpr *InitList);
59+
RecordInitListHelper(const CXXParenListInitExpr *ParenInitList);
5960

6061
// Base classes with their associated initializer expressions.
6162
ArrayRef<std::pair<const CXXBaseSpecifier *, Expr *>> base_inits() const {
@@ -68,6 +69,9 @@ class RecordInitListHelper {
6869
}
6970

7071
private:
72+
RecordInitListHelper(QualType Ty, std::vector<const FieldDecl *> Fields,
73+
ArrayRef<Expr *> Inits);
74+
7175
SmallVector<std::pair<const CXXBaseSpecifier *, Expr *>> BaseInits;
7276
SmallVector<std::pair<const FieldDecl *, Expr *>> FieldInits;
7377

clang/lib/Analysis/FlowSensitive/ASTOps.cpp

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,12 @@ bool containsSameFields(const FieldSet &Fields,
8080
}
8181

8282
/// Returns the fields of a `RecordDecl` that are initialized by an
83-
/// `InitListExpr`, in the order in which they appear in
84-
/// `InitListExpr::inits()`.
85-
/// `Init->getType()` must be a record type.
83+
/// `InitListExpr` or `CXXParenListInitExpr`, in the order in which they appear
84+
/// in `InitListExpr::inits()` / `CXXParenListInitExpr::getInitExprs()`.
85+
/// `InitList->getType()` must be a record type.
86+
template <class InitListT>
8687
static std::vector<const FieldDecl *>
87-
getFieldsForInitListExpr(const InitListExpr *InitList) {
88+
getFieldsForInitListExpr(const InitListT *InitList) {
8889
const RecordDecl *RD = InitList->getType()->getAsRecordDecl();
8990
assert(RD != nullptr);
9091

@@ -105,19 +106,29 @@ getFieldsForInitListExpr(const InitListExpr *InitList) {
105106
return Fields;
106107
}
107108

108-
RecordInitListHelper::RecordInitListHelper(const InitListExpr *InitList) {
109-
auto *RD = InitList->getType()->getAsCXXRecordDecl();
110-
assert(RD != nullptr);
109+
RecordInitListHelper::RecordInitListHelper(const InitListExpr *InitList)
110+
: RecordInitListHelper(InitList->getType(),
111+
getFieldsForInitListExpr(InitList),
112+
InitList->inits()) {}
113+
114+
RecordInitListHelper::RecordInitListHelper(
115+
const CXXParenListInitExpr *ParenInitList)
116+
: RecordInitListHelper(ParenInitList->getType(),
117+
getFieldsForInitListExpr(ParenInitList),
118+
ParenInitList->getInitExprs()) {}
111119

112-
std::vector<const FieldDecl *> Fields = getFieldsForInitListExpr(InitList);
113-
ArrayRef<Expr *> Inits = InitList->inits();
120+
RecordInitListHelper::RecordInitListHelper(
121+
QualType Ty, std::vector<const FieldDecl *> Fields,
122+
ArrayRef<Expr *> Inits) {
123+
auto *RD = Ty->getAsCXXRecordDecl();
124+
assert(RD != nullptr);
114125

115126
// Unions initialized with an empty initializer list need special treatment.
116127
// For structs/classes initialized with an empty initializer list, Clang
117128
// puts `ImplicitValueInitExpr`s in `InitListExpr::inits()`, but for unions,
118129
// it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves.
119130
SmallVector<Expr *> InitsForUnion;
120-
if (InitList->getType()->isUnionType() && Inits.empty()) {
131+
if (Ty->isUnionType() && Inits.empty()) {
121132
assert(Fields.size() == 1);
122133
ImplicitValueInitForUnion.emplace(Fields.front()->getType());
123134
InitsForUnion.push_back(&*ImplicitValueInitForUnion);
@@ -217,6 +228,10 @@ static void getReferencedDecls(const Stmt &S, ReferencedDecls &Referenced) {
217228
if (InitList->getType()->isRecordType())
218229
for (const auto *FD : getFieldsForInitListExpr(InitList))
219230
Referenced.Fields.insert(FD);
231+
} else if (auto *ParenInitList = dyn_cast<CXXParenListInitExpr>(&S)) {
232+
if (ParenInitList->getType()->isRecordType())
233+
for (const auto *FD : getFieldsForInitListExpr(ParenInitList))
234+
Referenced.Fields.insert(FD);
220235
}
221236
}
222237

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,28 @@ class ResultObjectVisitor : public RecursiveASTVisitor<ResultObjectVisitor> {
401401
return true;
402402
}
403403

404+
void
405+
PropagateResultObjectToRecordInitList(const RecordInitListHelper &InitList,
406+
RecordStorageLocation *Loc) {
407+
for (auto [Base, Init] : InitList.base_inits()) {
408+
assert(Base->getType().getCanonicalType() ==
409+
Init->getType().getCanonicalType());
410+
411+
// Storage location for the base class is the same as that of the
412+
// derived class because we "flatten" the object hierarchy and put all
413+
// fields in `RecordStorageLocation` of the derived class.
414+
PropagateResultObject(Init, Loc);
415+
}
416+
417+
for (auto [Field, Init] : InitList.field_inits()) {
418+
// Fields of non-record type are handled in
419+
// `TransferVisitor::VisitInitListExpr()`.
420+
if (Field->getType()->isRecordType())
421+
PropagateResultObject(Init,
422+
cast<RecordStorageLocation>(Loc->getChild(*Field)));
423+
}
424+
}
425+
404426
// Assigns `Loc` as the result object location of `E`, then propagates the
405427
// location to all lower-level prvalues that initialize the same object as
406428
// `E` (or one of its base classes or member variables).
@@ -440,26 +462,14 @@ class ResultObjectVisitor : public RecursiveASTVisitor<ResultObjectVisitor> {
440462
return;
441463
}
442464

443-
RecordInitListHelper InitListHelper(InitList);
444-
445-
for (auto [Base, Init] : InitListHelper.base_inits()) {
446-
assert(Base->getType().getCanonicalType() ==
447-
Init->getType().getCanonicalType());
448-
449-
// Storage location for the base class is the same as that of the
450-
// derived class because we "flatten" the object hierarchy and put all
451-
// fields in `RecordStorageLocation` of the derived class.
452-
PropagateResultObject(Init, Loc);
453-
}
465+
PropagateResultObjectToRecordInitList(RecordInitListHelper(InitList),
466+
Loc);
467+
return;
468+
}
454469

455-
for (auto [Field, Init] : InitListHelper.field_inits()) {
456-
// Fields of non-record type are handled in
457-
// `TransferVisitor::VisitInitListExpr()`.
458-
if (!Field->getType()->isRecordType())
459-
continue;
460-
PropagateResultObject(
461-
Init, cast<RecordStorageLocation>(Loc->getChild(*Field)));
462-
}
470+
if (auto *ParenInitList = dyn_cast<CXXParenListInitExpr>(E)) {
471+
PropagateResultObjectToRecordInitList(RecordInitListHelper(ParenInitList),
472+
Loc);
463473
return;
464474
}
465475

clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3098,6 +3098,79 @@ TEST(TransferTest, ResultObjectLocationForCXXOperatorCallExpr) {
30983098
});
30993099
}
31003100

3101+
TEST(TransferTest, ResultObjectLocationForInitListExpr) {
3102+
std::string Code = R"cc(
3103+
struct Inner {};
3104+
3105+
struct Outer { Inner I; };
3106+
3107+
void target() {
3108+
Outer O = { Inner() };
3109+
// [[p]]
3110+
}
3111+
)cc";
3112+
using ast_matchers::asString;
3113+
using ast_matchers::cxxConstructExpr;
3114+
using ast_matchers::hasType;
3115+
using ast_matchers::match;
3116+
using ast_matchers::selectFirst;
3117+
using ast_matchers::traverse;
3118+
runDataflow(
3119+
Code,
3120+
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3121+
ASTContext &ASTCtx) {
3122+
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
3123+
3124+
auto *Construct = selectFirst<CXXConstructExpr>(
3125+
"construct",
3126+
match(
3127+
cxxConstructExpr(hasType(asString("Inner"))).bind("construct"),
3128+
ASTCtx));
3129+
3130+
EXPECT_EQ(
3131+
&Env.getResultObjectLocation(*Construct),
3132+
&getFieldLoc(getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "O"),
3133+
"I", ASTCtx));
3134+
});
3135+
}
3136+
3137+
TEST(TransferTest, ResultObjectLocationForParenInitListExpr) {
3138+
std::string Code = R"cc(
3139+
struct Inner {};
3140+
3141+
struct Outer { Inner I; };
3142+
3143+
void target() {
3144+
Outer O((Inner()));
3145+
// [[p]]
3146+
}
3147+
)cc";
3148+
using ast_matchers::asString;
3149+
using ast_matchers::cxxConstructExpr;
3150+
using ast_matchers::hasType;
3151+
using ast_matchers::match;
3152+
using ast_matchers::selectFirst;
3153+
using ast_matchers::traverse;
3154+
runDataflow(
3155+
Code,
3156+
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3157+
ASTContext &ASTCtx) {
3158+
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
3159+
3160+
auto *Construct = selectFirst<CXXConstructExpr>(
3161+
"construct",
3162+
match(
3163+
cxxConstructExpr(hasType(asString("Inner"))).bind("construct"),
3164+
ASTCtx));
3165+
3166+
EXPECT_EQ(
3167+
&Env.getResultObjectLocation(*Construct),
3168+
&getFieldLoc(getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "O"),
3169+
"I", ASTCtx));
3170+
},
3171+
LangStandard::lang_cxx20);
3172+
}
3173+
31013174
// Check that the `std::strong_ordering` object returned by builtin `<=>` has a
31023175
// correctly modeled result object location.
31033176
TEST(TransferTest, ResultObjectLocationForBuiltinSpaceshipOperator) {

0 commit comments

Comments
 (0)