Skip to content

Commit cd64b0e

Browse files
committed
Reapply "[clang][dataflow] Correctly handle InitListExpr of union type." (#82856)
This reverts commit c4e9463.
1 parent aaec22f commit cd64b0e

File tree

5 files changed

+65
-20
lines changed

5 files changed

+65
-20
lines changed

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -723,9 +723,12 @@ RecordStorageLocation *getImplicitObjectLocation(const CXXMemberCallExpr &MCE,
723723
RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
724724
const Environment &Env);
725725

726-
/// Returns the fields of `RD` that are initialized by an `InitListExpr`, in the
727-
/// order in which they appear in `InitListExpr::inits()`.
728-
std::vector<FieldDecl *> getFieldsForInitListExpr(const RecordDecl *RD);
726+
/// Returns the fields of a `RecordDecl` that are initialized by an
727+
/// `InitListExpr`, in the order in which they appear in
728+
/// `InitListExpr::inits()`.
729+
/// `Init->getType()` must be a record type.
730+
std::vector<const FieldDecl *>
731+
getFieldsForInitListExpr(const InitListExpr *InitList);
729732

730733
/// Associates a new `RecordValue` with `Loc` and returns the new value.
731734
RecordValue &refreshRecordValue(RecordStorageLocation &Loc, Environment &Env);

clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,8 @@ getFieldsGlobalsAndFuncs(const Stmt &S, FieldSet &Fields,
361361
if (const auto *FD = dyn_cast<FieldDecl>(VD))
362362
Fields.insert(FD);
363363
} else if (auto *InitList = dyn_cast<InitListExpr>(&S)) {
364-
if (RecordDecl *RD = InitList->getType()->getAsRecordDecl())
365-
for (const auto *FD : getFieldsForInitListExpr(RD))
364+
if (InitList->getType()->isRecordType())
365+
for (const auto *FD : getFieldsForInitListExpr(InitList))
366366
Fields.insert(FD);
367367
}
368368
}
@@ -1104,12 +1104,22 @@ RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
11041104
return Env.get<RecordStorageLocation>(*Base);
11051105
}
11061106

1107-
std::vector<FieldDecl *> getFieldsForInitListExpr(const RecordDecl *RD) {
1107+
std::vector<const FieldDecl *>
1108+
getFieldsForInitListExpr(const InitListExpr *InitList) {
1109+
const RecordDecl *RD = InitList->getType()->getAsRecordDecl();
1110+
assert(RD != nullptr);
1111+
1112+
std::vector<const FieldDecl *> Fields;
1113+
1114+
if (InitList->getType()->isUnionType()) {
1115+
Fields.push_back(InitList->getInitializedFieldInUnion());
1116+
return Fields;
1117+
}
1118+
11081119
// Unnamed bitfields are only used for padding and do not appear in
11091120
// `InitListExpr`'s inits. However, those fields do appear in `RecordDecl`'s
11101121
// field list, and we thus need to remove them before mapping inits to
11111122
// fields to avoid mapping inits to the wrongs fields.
1112-
std::vector<FieldDecl *> Fields;
11131123
llvm::copy_if(
11141124
RD->fields(), std::back_inserter(Fields),
11151125
[](const FieldDecl *Field) { return !Field->isUnnamedBitfield(); });

clang/lib/Analysis/FlowSensitive/Transfer.cpp

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -663,14 +663,7 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
663663
void VisitInitListExpr(const InitListExpr *S) {
664664
QualType Type = S->getType();
665665

666-
if (Type->isUnionType()) {
667-
// FIXME: Initialize unions properly.
668-
if (auto *Val = Env.createValue(Type))
669-
Env.setValue(*S, *Val);
670-
return;
671-
}
672-
673-
if (!Type->isStructureOrClassType()) {
666+
if (!Type->isRecordType()) {
674667
// Until array initialization is implemented, we skip arrays and don't
675668
// need to care about cases where `getNumInits() > 1`.
676669
if (!Type->isArrayType() && S->getNumInits() == 1)
@@ -688,10 +681,9 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
688681
llvm::DenseMap<const ValueDecl *, StorageLocation *> FieldLocs;
689682

690683
// This only contains the direct fields for the given type.
691-
std::vector<FieldDecl *> FieldsForInit =
692-
getFieldsForInitListExpr(Type->getAsRecordDecl());
684+
std::vector<const FieldDecl *> FieldsForInit = getFieldsForInitListExpr(S);
693685

694-
// `S->inits()` contains all the initializer epressions, including the
686+
// `S->inits()` contains all the initializer expressions, including the
695687
// ones for direct base classes.
696688
auto Inits = S->inits();
697689
size_t InitIdx = 0;
@@ -731,6 +723,17 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
731723
FieldLocs.insert({Field, &Loc});
732724
}
733725

726+
// In the case of a union, we don't in general have initializers for all
727+
// of the fields. Create storage locations for the remaining fields (but
728+
// don't associate them with values).
729+
if (Type->isUnionType()) {
730+
for (const FieldDecl *Field :
731+
Env.getDataflowAnalysisContext().getModeledFields(Type)) {
732+
if (auto [it, inserted] = FieldLocs.insert({Field, nullptr}); inserted)
733+
it->second = &Env.createStorageLocation(Field->getType());
734+
}
735+
}
736+
734737
// Check that we satisfy the invariant that a `RecordStorageLoation`
735738
// contains exactly the set of modeled fields for that type.
736739
// `ModeledFields` includes fields from all the bases, but only the

clang/unittests/Analysis/FlowSensitive/TestingSupport.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,8 @@ llvm::Error checkDataflowWithNoopAnalysis(
432432
{});
433433

434434
/// Returns the `ValueDecl` for the given identifier.
435+
/// The returned pointer is guaranteed to be non-null; the function asserts if
436+
/// no `ValueDecl` with the given name is found.
435437
///
436438
/// Requirements:
437439
///
@@ -475,6 +477,15 @@ ValueT &getValueForDecl(ASTContext &ASTCtx, const Environment &Env,
475477
return *cast<ValueT>(Env.getValue(*VD));
476478
}
477479

480+
/// Returns the storage location for the field called `Name` of `Loc`.
481+
/// Optionally casts the field storage location to `T`.
482+
template <typename T = StorageLocation>
483+
std::enable_if_t<std::is_base_of_v<StorageLocation, T>, T &>
484+
getFieldLoc(const RecordStorageLocation &Loc, llvm::StringRef Name,
485+
ASTContext &ASTCtx) {
486+
return *cast<T>(Loc.getChild(*findValueDecl(ASTCtx, Name)));
487+
}
488+
478489
/// Returns the value of a `Field` on the record referenced by `Loc.`
479490
/// Returns null if `Loc` is null.
480491
inline Value *getFieldValue(const RecordStorageLocation *Loc,
@@ -487,6 +498,14 @@ inline Value *getFieldValue(const RecordStorageLocation *Loc,
487498
return Env.getValue(*FieldLoc);
488499
}
489500

501+
/// Returns the value of a `Field` on the record referenced by `Loc.`
502+
/// Returns null if `Loc` is null.
503+
inline Value *getFieldValue(const RecordStorageLocation *Loc,
504+
llvm::StringRef Name, ASTContext &ASTCtx,
505+
const Environment &Env) {
506+
return getFieldValue(Loc, *findValueDecl(ASTCtx, Name), Env);
507+
}
508+
490509
/// Creates and owns constraints which are boolean values.
491510
class ConstraintContext {
492511
unsigned NextAtom = 0;

clang/unittests/Analysis/FlowSensitive/TransferTest.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2392,14 +2392,24 @@ TEST(TransferTest, InitListExprAsUnion) {
23922392
} F;
23932393
23942394
public:
2395-
constexpr target() : F{nullptr} {}
2395+
constexpr target() : F{nullptr} {
2396+
int *null = nullptr;
2397+
F.b; // Make sure we reference 'b' so it is modeled.
2398+
// [[p]]
2399+
}
23962400
};
23972401
)cc";
23982402
runDataflow(
23992403
Code,
24002404
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
24012405
ASTContext &ASTCtx) {
2402-
// Just verify that it doesn't crash.
2406+
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
2407+
2408+
auto &FLoc = getFieldLoc<RecordStorageLocation>(
2409+
*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
2410+
auto *AVal = cast<PointerValue>(getFieldValue(&FLoc, "a", ASTCtx, Env));
2411+
ASSERT_EQ(AVal, &getValueForDecl<PointerValue>(ASTCtx, Env, "null"));
2412+
ASSERT_EQ(getFieldValue(&FLoc, "b", ASTCtx, Env), nullptr);
24032413
});
24042414
}
24052415

0 commit comments

Comments
 (0)