Skip to content

Commit 128780b

Browse files
authored
[clang][dataflow] Correctly treat empty initializer lists for unions. (#82986)
This fixes a crash introduced by #82348 but also adds additional handling to make sure that we treat empty initializer lists for both unions and structs/classes correctly (see tests added in this patch).
1 parent 420928b commit 128780b

File tree

5 files changed

+153
-22
lines changed

5 files changed

+153
-22
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: 20 additions & 5 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
}
@@ -983,7 +983,7 @@ StorageLocation &Environment::createObjectInternal(const ValueDecl *D,
983983
}
984984

985985
Value *Val = nullptr;
986-
if (InitExpr)
986+
if (InitExpr) {
987987
// In the (few) cases where an expression is intentionally
988988
// "uninterpreted", `InitExpr` is not associated with a value. There are
989989
// two ways to handle this situation: propagate the status, so that
@@ -998,6 +998,11 @@ StorageLocation &Environment::createObjectInternal(const ValueDecl *D,
998998
// default value (assuming we don't update the environment API to return
999999
// references).
10001000
Val = getValue(*InitExpr);
1001+
1002+
if (!Val && isa<ImplicitValueInitExpr>(InitExpr) &&
1003+
InitExpr->getType()->isPointerType())
1004+
Val = &getOrCreateNullPointerValue(InitExpr->getType()->getPointeeType());
1005+
}
10011006
if (!Val)
10021007
Val = createValue(Ty);
10031008

@@ -1104,12 +1109,22 @@ RecordStorageLocation *getBaseObjectLocation(const MemberExpr &ME,
11041109
return Env.get<RecordStorageLocation>(*Base);
11051110
}
11061111

1107-
std::vector<FieldDecl *> getFieldsForInitListExpr(const RecordDecl *RD) {
1112+
std::vector<const FieldDecl *>
1113+
getFieldsForInitListExpr(const InitListExpr *InitList) {
1114+
const RecordDecl *RD = InitList->getType()->getAsRecordDecl();
1115+
assert(RD != nullptr);
1116+
1117+
std::vector<const FieldDecl *> Fields;
1118+
1119+
if (InitList->getType()->isUnionType()) {
1120+
Fields.push_back(InitList->getInitializedFieldInUnion());
1121+
return Fields;
1122+
}
1123+
11081124
// Unnamed bitfields are only used for padding and do not appear in
11091125
// `InitListExpr`'s inits. However, those fields do appear in `RecordDecl`'s
11101126
// field list, and we thus need to remove them before mapping inits to
11111127
// fields to avoid mapping inits to the wrongs fields.
1112-
std::vector<FieldDecl *> Fields;
11131128
llvm::copy_if(
11141129
RD->fields(), std::back_inserter(Fields),
11151130
[](const FieldDecl *Field) { return !Field->isUnnamedBitfield(); });

clang/lib/Analysis/FlowSensitive/Transfer.cpp

Lines changed: 28 additions & 12 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,14 +681,26 @@ 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.
696-
auto Inits = S->inits();
688+
ArrayRef<Expr *> Inits = S->inits();
697689
size_t InitIdx = 0;
698690

691+
// Unions initialized with an empty initializer list need special treatment.
692+
// For structs/classes initialized with an empty initializer list, Clang
693+
// puts `ImplicitValueInitExpr`s in `InitListExpr::inits()`, but for unions,
694+
// it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves.
695+
std::optional<ImplicitValueInitExpr> ImplicitValueInitForUnion;
696+
SmallVector<Expr *> InitsForUnion;
697+
if (S->getType()->isUnionType() && Inits.empty()) {
698+
assert(FieldsForInit.size() == 1);
699+
ImplicitValueInitForUnion.emplace(FieldsForInit.front()->getType());
700+
InitsForUnion.push_back(&*ImplicitValueInitForUnion);
701+
Inits = InitsForUnion;
702+
}
703+
699704
// Initialize base classes.
700705
if (auto* R = S->getType()->getAsCXXRecordDecl()) {
701706
assert(FieldsForInit.size() + R->getNumBases() == Inits.size());
@@ -731,6 +736,17 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
731736
FieldLocs.insert({Field, &Loc});
732737
}
733738

739+
// In the case of a union, we don't in general have initializers for all
740+
// of the fields. Create storage locations for the remaining fields (but
741+
// don't associate them with values).
742+
if (Type->isUnionType()) {
743+
for (const FieldDecl *Field :
744+
Env.getDataflowAnalysisContext().getModeledFields(Type)) {
745+
if (auto [it, inserted] = FieldLocs.insert({Field, nullptr}); inserted)
746+
it->second = &Env.createStorageLocation(Field->getType());
747+
}
748+
}
749+
734750
// Check that we satisfy the invariant that a `RecordStorageLoation`
735751
// contains exactly the set of modeled fields for that type.
736752
// `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: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2392,14 +2392,92 @@ 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+
EXPECT_EQ(AVal, &getValueForDecl<PointerValue>(ASTCtx, Env, "null"));
2412+
EXPECT_EQ(getFieldValue(&FLoc, "b", ASTCtx, Env), nullptr);
2413+
});
2414+
}
2415+
2416+
TEST(TransferTest, EmptyInitListExprForUnion) {
2417+
// This is a crash repro.
2418+
std::string Code = R"cc(
2419+
class target {
2420+
union {
2421+
int *a;
2422+
bool *b;
2423+
} F;
2424+
2425+
public:
2426+
// Empty initializer list means that `F` is aggregate-initialized.
2427+
// For a union, this has the effect that the first member of the union
2428+
// is copy-initialized from an empty initializer list; in this specific
2429+
// case, this has the effect of initializing `a` with null.
2430+
constexpr target() : F{} {
2431+
int *null = nullptr;
2432+
F.b; // Make sure we reference 'b' so it is modeled.
2433+
// [[p]]
2434+
}
2435+
};
2436+
)cc";
2437+
runDataflow(
2438+
Code,
2439+
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2440+
ASTContext &ASTCtx) {
2441+
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
2442+
2443+
auto &FLoc = getFieldLoc<RecordStorageLocation>(
2444+
*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
2445+
auto *AVal = cast<PointerValue>(getFieldValue(&FLoc, "a", ASTCtx, Env));
2446+
EXPECT_EQ(AVal, &getValueForDecl<PointerValue>(ASTCtx, Env, "null"));
2447+
EXPECT_EQ(getFieldValue(&FLoc, "b", ASTCtx, Env), nullptr);
2448+
});
2449+
}
2450+
2451+
TEST(TransferTest, EmptyInitListExprForStruct) {
2452+
std::string Code = R"cc(
2453+
class target {
2454+
struct {
2455+
int *a;
2456+
bool *b;
2457+
} F;
2458+
2459+
public:
2460+
constexpr target() : F{} {
2461+
int *NullIntPtr = nullptr;
2462+
bool *NullBoolPtr = nullptr;
2463+
// [[p]]
2464+
}
2465+
};
2466+
)cc";
2467+
runDataflow(
2468+
Code,
2469+
[](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2470+
ASTContext &ASTCtx) {
2471+
const Environment &Env = getEnvironmentAtAnnotation(Results, "p");
2472+
2473+
auto &FLoc = getFieldLoc<RecordStorageLocation>(
2474+
*Env.getThisPointeeStorageLocation(), "F", ASTCtx);
2475+
auto *AVal = cast<PointerValue>(getFieldValue(&FLoc, "a", ASTCtx, Env));
2476+
EXPECT_EQ(AVal,
2477+
&getValueForDecl<PointerValue>(ASTCtx, Env, "NullIntPtr"));
2478+
auto *BVal = cast<PointerValue>(getFieldValue(&FLoc, "b", ASTCtx, Env));
2479+
EXPECT_EQ(BVal,
2480+
&getValueForDecl<PointerValue>(ASTCtx, Env, "NullBoolPtr"));
24032481
});
24042482
}
24052483

0 commit comments

Comments
 (0)