Skip to content

Commit 3825a7c

Browse files
authored
[clang][Interp] Fix diagnosing uninitialized nested union fields (#102824)
We were calling initialize() unconditionally when copying the union.
1 parent 7027cc6 commit 3825a7c

File tree

3 files changed

+88
-39
lines changed

3 files changed

+88
-39
lines changed

clang/lib/AST/Interp/Interp.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,17 @@ static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
126126
return true;
127127

128128
assert(Ptr.inUnion());
129+
assert(Ptr.isField() && Ptr.getField());
129130

130131
Pointer U = Ptr.getBase();
131132
Pointer C = Ptr;
132133
while (!U.isRoot() && U.inUnion() && !U.isActive()) {
133-
C = U;
134+
if (U.getField())
135+
C = U;
134136
U = U.getBase();
135137
}
138+
assert(C.isField());
139+
136140
// Get the inactive field descriptor.
137141
const FieldDecl *InactiveField = C.getField();
138142
assert(InactiveField);

clang/lib/AST/Interp/InterpBuiltin.cpp

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,7 +1635,58 @@ bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
16351635
return true;
16361636
}
16371637

1638-
bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest) {
1638+
static bool copyComposite(InterpState &S, CodePtr OpPC, const Pointer &Src,
1639+
Pointer &Dest, bool Activate);
1640+
static bool copyRecord(InterpState &S, CodePtr OpPC, const Pointer &Src,
1641+
Pointer &Dest, bool Activate = false) {
1642+
[[maybe_unused]] const Descriptor *SrcDesc = Src.getFieldDesc();
1643+
const Descriptor *DestDesc = Dest.getFieldDesc();
1644+
1645+
auto copyField = [&](const Record::Field &F, bool Activate) -> bool {
1646+
Pointer DestField = Dest.atField(F.Offset);
1647+
if (std::optional<PrimType> FT = S.Ctx.classify(F.Decl->getType())) {
1648+
TYPE_SWITCH(*FT, {
1649+
DestField.deref<T>() = Src.atField(F.Offset).deref<T>();
1650+
if (Src.atField(F.Offset).isInitialized())
1651+
DestField.initialize();
1652+
if (Activate)
1653+
DestField.activate();
1654+
});
1655+
return true;
1656+
}
1657+
// Composite field.
1658+
return copyComposite(S, OpPC, Src.atField(F.Offset), DestField, Activate);
1659+
};
1660+
1661+
assert(SrcDesc->isRecord());
1662+
assert(SrcDesc->ElemRecord == DestDesc->ElemRecord);
1663+
const Record *R = DestDesc->ElemRecord;
1664+
for (const Record::Field &F : R->fields()) {
1665+
if (R->isUnion()) {
1666+
// For unions, only copy the active field.
1667+
const Pointer &SrcField = Src.atField(F.Offset);
1668+
if (SrcField.isActive()) {
1669+
if (!copyField(F, /*Activate=*/true))
1670+
return false;
1671+
}
1672+
} else {
1673+
if (!copyField(F, Activate))
1674+
return false;
1675+
}
1676+
}
1677+
1678+
for (const Record::Base &B : R->bases()) {
1679+
Pointer DestBase = Dest.atField(B.Offset);
1680+
if (!copyRecord(S, OpPC, Src.atField(B.Offset), DestBase, Activate))
1681+
return false;
1682+
}
1683+
1684+
Dest.initialize();
1685+
return true;
1686+
}
1687+
1688+
static bool copyComposite(InterpState &S, CodePtr OpPC, const Pointer &Src,
1689+
Pointer &Dest, bool Activate = false) {
16391690
assert(Src.isLive() && Dest.isLive());
16401691

16411692
[[maybe_unused]] const Descriptor *SrcDesc = Src.getFieldDesc();
@@ -1657,44 +1708,14 @@ bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest) {
16571708
return true;
16581709
}
16591710

1660-
if (DestDesc->isRecord()) {
1661-
auto copyField = [&](const Record::Field &F, bool Activate) -> bool {
1662-
Pointer DestField = Dest.atField(F.Offset);
1663-
if (std::optional<PrimType> FT = S.Ctx.classify(F.Decl->getType())) {
1664-
TYPE_SWITCH(*FT, {
1665-
DestField.deref<T>() = Src.atField(F.Offset).deref<T>();
1666-
DestField.initialize();
1667-
if (Activate)
1668-
DestField.activate();
1669-
});
1670-
return true;
1671-
}
1672-
return Invalid(S, OpPC);
1673-
};
1674-
1675-
assert(SrcDesc->isRecord());
1676-
assert(SrcDesc->ElemRecord == DestDesc->ElemRecord);
1677-
const Record *R = DestDesc->ElemRecord;
1678-
for (const Record::Field &F : R->fields()) {
1679-
if (R->isUnion()) {
1680-
// For unions, only copy the active field.
1681-
const Pointer &SrcField = Src.atField(F.Offset);
1682-
if (SrcField.isActive()) {
1683-
if (!copyField(F, /*Activate=*/true))
1684-
return false;
1685-
}
1686-
} else {
1687-
if (!copyField(F, /*Activate=*/false))
1688-
return false;
1689-
}
1690-
}
1691-
return true;
1692-
}
1693-
1694-
// FIXME: Composite types.
1695-
1711+
if (DestDesc->isRecord())
1712+
return copyRecord(S, OpPC, Src, Dest, Activate);
16961713
return Invalid(S, OpPC);
16971714
}
16981715

1716+
bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest) {
1717+
return copyComposite(S, OpPC, Src, Dest);
1718+
}
1719+
16991720
} // namespace interp
17001721
} // namespace clang

clang/test/AST/Interp/unions.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ namespace CopyCtor {
361361

362362
namespace UnionInBase {
363363
struct Base {
364-
int y;
364+
int y; // both-note {{subobject declared here}}
365365
};
366366
struct A : Base {
367367
int x;
@@ -380,5 +380,29 @@ namespace UnionInBase {
380380
}
381381
static_assert(read_wrong_member_indirect() == 1); // both-error {{not an integral constant expression}} \
382382
// both-note {{in call to}}
383+
constexpr int read_uninitialized() {
384+
B b = {.b = 1};
385+
int *p = &b.a.y;
386+
b.a.x = 1;
387+
return *p; // both-note {{read of uninitialized object}}
388+
}
389+
static_assert(read_uninitialized() == 0); // both-error {{constant}} \
390+
// both-note {{in call}}
391+
constexpr int write_uninitialized() {
392+
B b = {.b = 1};
393+
int *p = &b.a.y;
394+
b.a.x = 1;
395+
*p = 1;
396+
return *p;
397+
}
398+
399+
constexpr B return_uninit() {
400+
B b = {.b = 1};
401+
b.a.x = 2;
402+
return b;
403+
}
404+
constexpr B uninit = return_uninit(); // both-error {{constant expression}} \
405+
// both-note {{subobject 'y' is not initialized}}
406+
static_assert(return_uninit().a.x == 2);
383407
}
384408
#endif

0 commit comments

Comments
 (0)