Skip to content

Commit 496b224

Browse files
authored
[clang][Interp] Handle union copy/move ctors (#102762)
They don't have a body and we need to implement them ourselves. Use the Memcpy op to do that.
1 parent 4ce2f98 commit 496b224

File tree

3 files changed

+48
-5
lines changed

3 files changed

+48
-5
lines changed

clang/lib/AST/Interp/Compiler.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4775,6 +4775,22 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
47754775
if (!R)
47764776
return false;
47774777

4778+
if (R->isUnion() && Ctor->isCopyOrMoveConstructor()) {
4779+
// union copy and move ctors are special.
4780+
assert(cast<CompoundStmt>(Ctor->getBody())->body_empty());
4781+
if (!this->emitThis(Ctor))
4782+
return false;
4783+
4784+
auto PVD = Ctor->getParamDecl(0);
4785+
ParamOffset PO = this->Params[PVD]; // Must exist.
4786+
4787+
if (!this->emitGetParam(PT_Ptr, PO.Offset, Ctor))
4788+
return false;
4789+
4790+
return this->emitMemcpy(Ctor) && this->emitPopPtr(Ctor) &&
4791+
this->emitRetVoid(Ctor);
4792+
}
4793+
47784794
InitLinkScope<Emitter> InitScope(this, InitLink::This());
47794795
for (const auto *Init : Ctor->inits()) {
47804796
// Scope needed for the initializers.

clang/lib/AST/Interp/InterpBuiltin.cpp

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,18 +1658,34 @@ bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest) {
16581658
}
16591659

16601660
if (DestDesc->isRecord()) {
1661-
assert(SrcDesc->isRecord());
1662-
assert(SrcDesc->ElemRecord == DestDesc->ElemRecord);
1663-
const Record *R = DestDesc->ElemRecord;
1664-
for (const Record::Field &F : R->fields()) {
1661+
auto copyField = [&](const Record::Field &F, bool Activate) -> bool {
16651662
Pointer DestField = Dest.atField(F.Offset);
16661663
if (std::optional<PrimType> FT = S.Ctx.classify(F.Decl->getType())) {
16671664
TYPE_SWITCH(*FT, {
16681665
DestField.deref<T>() = Src.atField(F.Offset).deref<T>();
16691666
DestField.initialize();
1667+
if (Activate)
1668+
DestField.activate();
16701669
});
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+
}
16711686
} else {
1672-
return Invalid(S, OpPC);
1687+
if (!copyField(F, /*Activate=*/false))
1688+
return false;
16731689
}
16741690
}
16751691
return true;

clang/test/AST/Interp/unions.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,5 +346,16 @@ namespace IndirectField {
346346
static_assert(s2.f == 7, "");
347347
}
348348

349+
namespace CopyCtor {
350+
union U {
351+
int a;
352+
int b;
353+
};
349354

355+
constexpr U x = {42};
356+
constexpr U y = x;
357+
static_assert(y.a == 42, "");
358+
static_assert(y.b == 42, ""); // both-error {{constant expression}} \
359+
// both-note {{'b' of union with active member 'a'}}
360+
}
350361
#endif

0 commit comments

Comments
 (0)