Skip to content

Commit 8d77d36

Browse files
authored
[clang][dataflow] Introduce a helper class for handling record initializer lists. (#86675)
This is currently only used in one place, but I'm working on a patch that will use this from a second place. And I think this already improves the readability of the one place this is used so far.
1 parent eff4593 commit 8d77d36

File tree

3 files changed

+81
-45
lines changed

3 files changed

+81
-45
lines changed

clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,35 @@ RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
744744
std::vector<const FieldDecl *>
745745
getFieldsForInitListExpr(const InitListExpr *InitList);
746746

747+
/// Helper class for initialization of a record with an `InitListExpr`.
748+
/// `InitListExpr::inits()` contains the initializers for both the base classes
749+
/// and the fields of the record; this helper class separates these out into two
750+
/// different lists. In addition, it deals with special cases associated with
751+
/// unions.
752+
class RecordInitListHelper {
753+
public:
754+
// `InitList` must have record type.
755+
RecordInitListHelper(const InitListExpr *InitList);
756+
757+
// Base classes with their associated initializer expressions.
758+
ArrayRef<std::pair<const CXXBaseSpecifier *, Expr *>> base_inits() const {
759+
return BaseInits;
760+
}
761+
762+
// Fields with their associated initializer expressions.
763+
ArrayRef<std::pair<const FieldDecl *, Expr *>> field_inits() const {
764+
return FieldInits;
765+
}
766+
767+
private:
768+
SmallVector<std::pair<const CXXBaseSpecifier *, Expr *>> BaseInits;
769+
SmallVector<std::pair<const FieldDecl *, Expr *>> FieldInits;
770+
771+
// We potentially synthesize an `ImplicitValueInitExpr` for unions. It's a
772+
// member variable because we store a pointer to it in `FieldInits`.
773+
std::optional<ImplicitValueInitExpr> ImplicitValueInitForUnion;
774+
};
775+
747776
/// Associates a new `RecordValue` with `Loc` and returns the new value.
748777
RecordValue &refreshRecordValue(RecordStorageLocation &Loc, Environment &Env);
749778

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,42 @@ getFieldsForInitListExpr(const InitListExpr *InitList) {
11691169
return Fields;
11701170
}
11711171

1172+
RecordInitListHelper::RecordInitListHelper(const InitListExpr *InitList) {
1173+
auto *RD = InitList->getType()->getAsCXXRecordDecl();
1174+
assert(RD != nullptr);
1175+
1176+
std::vector<const FieldDecl *> Fields = getFieldsForInitListExpr(InitList);
1177+
ArrayRef<Expr *> Inits = InitList->inits();
1178+
1179+
// Unions initialized with an empty initializer list need special treatment.
1180+
// For structs/classes initialized with an empty initializer list, Clang
1181+
// puts `ImplicitValueInitExpr`s in `InitListExpr::inits()`, but for unions,
1182+
// it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves.
1183+
SmallVector<Expr *> InitsForUnion;
1184+
if (InitList->getType()->isUnionType() && Inits.empty()) {
1185+
assert(Fields.size() == 1);
1186+
ImplicitValueInitForUnion.emplace(Fields.front()->getType());
1187+
InitsForUnion.push_back(&*ImplicitValueInitForUnion);
1188+
Inits = InitsForUnion;
1189+
}
1190+
1191+
size_t InitIdx = 0;
1192+
1193+
assert(Fields.size() + RD->getNumBases() == Inits.size());
1194+
for (const CXXBaseSpecifier &Base : RD->bases()) {
1195+
assert(InitIdx < Inits.size());
1196+
Expr *Init = Inits[InitIdx++];
1197+
BaseInits.emplace_back(&Base, Init);
1198+
}
1199+
1200+
assert(Fields.size() == Inits.size() - InitIdx);
1201+
for (const FieldDecl *Field : Fields) {
1202+
assert(InitIdx < Inits.size());
1203+
Expr *Init = Inits[InitIdx++];
1204+
FieldInits.emplace_back(Field, Init);
1205+
}
1206+
}
1207+
11721208
RecordValue &refreshRecordValue(RecordStorageLocation &Loc, Environment &Env) {
11731209
auto &NewVal = Env.create<RecordValue>(Loc);
11741210
Env.setValue(Loc, NewVal);

clang/lib/Analysis/FlowSensitive/Transfer.cpp

Lines changed: 16 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -689,51 +689,22 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
689689
}
690690

691691
llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;
692-
693-
// This only contains the direct fields for the given type.
694-
std::vector<const FieldDecl *> FieldsForInit = getFieldsForInitListExpr(S);
695-
696-
// `S->inits()` contains all the initializer expressions, including the
697-
// ones for direct base classes.
698-
ArrayRef<Expr *> Inits = S->inits();
699-
size_t InitIdx = 0;
700-
701-
// Unions initialized with an empty initializer list need special treatment.
702-
// For structs/classes initialized with an empty initializer list, Clang
703-
// puts `ImplicitValueInitExpr`s in `InitListExpr::inits()`, but for unions,
704-
// it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves.
705-
std::optional<ImplicitValueInitExpr> ImplicitValueInitForUnion;
706-
SmallVector<Expr *> InitsForUnion;
707-
if (S->getType()->isUnionType() && Inits.empty()) {
708-
assert(FieldsForInit.size() == 1);
709-
ImplicitValueInitForUnion.emplace(FieldsForInit.front()->getType());
710-
InitsForUnion.push_back(&*ImplicitValueInitForUnion);
711-
Inits = InitsForUnion;
712-
}
713-
714-
// Initialize base classes.
715-
if (auto* R = S->getType()->getAsCXXRecordDecl()) {
716-
assert(FieldsForInit.size() + R->getNumBases() == Inits.size());
717-
for ([[maybe_unused]] const CXXBaseSpecifier &Base : R->bases()) {
718-
assert(InitIdx < Inits.size());
719-
auto Init = Inits[InitIdx++];
720-
assert(Base.getType().getCanonicalType() ==
721-
Init->getType().getCanonicalType());
722-
auto *BaseVal = Env.get<RecordValue>(*Init);
723-
if (!BaseVal)
724-
BaseVal = cast<RecordValue>(Env.createValue(Init->getType()));
725-
// Take ownership of the fields of the `RecordValue` for the base class
726-
// and incorporate them into the "flattened" set of fields for the
727-
// derived class.
728-
auto Children = BaseVal->getLoc().children();
729-
FieldLocs.insert(Children.begin(), Children.end());
730-
}
731-
}
732-
733-
assert(FieldsForInit.size() == Inits.size() - InitIdx);
734-
for (auto Field : FieldsForInit) {
735-
assert(InitIdx < Inits.size());
736-
auto Init = Inits[InitIdx++];
692+
RecordInitListHelper InitListHelper(S);
693+
694+
for (auto [Base, Init] : InitListHelper.base_inits()) {
695+
assert(Base->getType().getCanonicalType() ==
696+
Init->getType().getCanonicalType());
697+
auto *BaseVal = Env.get<RecordValue>(*Init);
698+
if (!BaseVal)
699+
BaseVal = cast<RecordValue>(Env.createValue(Init->getType()));
700+
// Take ownership of the fields of the `RecordValue` for the base class
701+
// and incorporate them into the "flattened" set of fields for the
702+
// derived class.
703+
auto Children = BaseVal->getLoc().children();
704+
FieldLocs.insert(Children.begin(), Children.end());
705+
}
706+
707+
for (auto [Field, Init] : InitListHelper.field_inits()) {
737708
assert(
738709
// The types are same, or
739710
Field->getType().getCanonicalType().getUnqualifiedType() ==

0 commit comments

Comments
 (0)