Skip to content

Commit af68120

Browse files
committed
[clang][Interp] Fix initializing a union from an InitLIstExpr
We can't just count the field to initialize but need to consult the InitListExpr to give us the right field.
1 parent 58ddf3a commit af68120

File tree

3 files changed

+68
-30
lines changed

3 files changed

+68
-30
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,38 +1050,71 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
10501050
if (T->isRecordType()) {
10511051
const Record *R = getRecord(E->getType());
10521052

1053-
if (Inits.size() == 1 && E->getType() == Inits[0]->getType()) {
1053+
if (Inits.size() == 1 && E->getType() == Inits[0]->getType())
10541054
return this->visitInitializer(Inits[0]);
1055+
1056+
auto initPrimitiveField = [=](const Record::Field *FieldToInit,
1057+
const Expr *Init, PrimType T) -> bool {
1058+
if (!this->visit(Init))
1059+
return false;
1060+
1061+
if (FieldToInit->isBitField()) {
1062+
if (!this->emitInitBitField(T, FieldToInit, E))
1063+
return false;
1064+
} else {
1065+
if (!this->emitInitField(T, FieldToInit->Offset, E))
1066+
return false;
1067+
}
1068+
return this->emitPopPtr(E);
1069+
};
1070+
1071+
auto initCompositeField = [=](const Record::Field *FieldToInit,
1072+
const Expr *Init) -> bool {
1073+
// Non-primitive case. Get a pointer to the field-to-initialize
1074+
// on the stack and recurse into visitInitializer().
1075+
if (!this->emitGetPtrField(FieldToInit->Offset, Init))
1076+
return false;
1077+
if (!this->visitInitializer(Init))
1078+
return false;
1079+
return this->emitPopPtr(E);
1080+
};
1081+
1082+
if (R->isUnion()) {
1083+
assert(Inits.size() == 1);
1084+
const Expr *Init = Inits[0];
1085+
const FieldDecl *FToInit = nullptr;
1086+
if (const auto *ILE = dyn_cast<InitListExpr>(E))
1087+
FToInit = ILE->getInitializedFieldInUnion();
1088+
else
1089+
FToInit = cast<CXXParenListInitExpr>(E)->getInitializedFieldInUnion();
1090+
1091+
if (!this->emitDupPtr(E))
1092+
return false;
1093+
1094+
const Record::Field *FieldToInit = R->getField(FToInit);
1095+
if (std::optional<PrimType> T = classify(Init)) {
1096+
if (!initPrimitiveField(FieldToInit, Init, *T))
1097+
return false;
1098+
} else {
1099+
if (!initCompositeField(FieldToInit, Init))
1100+
return false;
1101+
}
1102+
return this->emitFinishInit(E);
10551103
}
10561104

1105+
assert(!R->isUnion());
10571106
unsigned InitIndex = 0;
10581107
for (const Expr *Init : Inits) {
10591108
// Skip unnamed bitfields.
10601109
while (InitIndex < R->getNumFields() &&
10611110
R->getField(InitIndex)->Decl->isUnnamedBitField())
10621111
++InitIndex;
1063-
1064-
// Potentially skip ahead. This is especially relevant in unions.
1065-
if (const auto *D = dyn_cast<CXXDefaultInitExpr>(Init))
1066-
InitIndex = D->getField()->getFieldIndex();
1067-
10681112
if (!this->emitDupPtr(E))
10691113
return false;
10701114

10711115
if (std::optional<PrimType> T = classify(Init)) {
10721116
const Record::Field *FieldToInit = R->getField(InitIndex);
1073-
if (!this->visit(Init))
1074-
return false;
1075-
1076-
if (FieldToInit->isBitField()) {
1077-
if (!this->emitInitBitField(*T, FieldToInit, E))
1078-
return false;
1079-
} else {
1080-
if (!this->emitInitField(*T, FieldToInit->Offset, E))
1081-
return false;
1082-
}
1083-
1084-
if (!this->emitPopPtr(E))
1117+
if (!initPrimitiveField(FieldToInit, Init, *T))
10851118
return false;
10861119
++InitIndex;
10871120
} else {
@@ -1099,21 +1132,13 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
10991132
// into the Record's fields.
11001133
} else {
11011134
const Record::Field *FieldToInit = R->getField(InitIndex);
1102-
// Non-primitive case. Get a pointer to the field-to-initialize
1103-
// on the stack and recurse into visitInitializer().
1104-
if (!this->emitGetPtrField(FieldToInit->Offset, Init))
1105-
return false;
1106-
1107-
if (!this->visitInitializer(Init))
1108-
return false;
1109-
1110-
if (!this->emitPopPtr(E))
1135+
if (!initCompositeField(FieldToInit, Init))
11111136
return false;
11121137
++InitIndex;
11131138
}
11141139
}
11151140
}
1116-
return true;
1141+
return this->emitFinishInit(E);
11171142
}
11181143

11191144
if (T->isArrayType()) {
@@ -1137,7 +1162,7 @@ bool ByteCodeExprGen<Emitter>::visitInitList(ArrayRef<const Expr *> Inits,
11371162
}
11381163
}
11391164

1140-
return true;
1165+
return this->emitFinishInit(E);
11411166
}
11421167

11431168
if (const auto *ComplexTy = E->getType()->getAs<ComplexType>()) {

clang/lib/AST/Interp/Interp.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1337,14 +1337,15 @@ inline bool FinishInitPop(InterpState &S, CodePtr OpPC) {
13371337
const Pointer &Ptr = S.Stk.pop<Pointer>();
13381338
if (Ptr.canBeInitialized())
13391339
Ptr.initialize();
1340+
Ptr.activate();
13401341
return true;
13411342
}
13421343

13431344
inline bool FinishInit(InterpState &S, CodePtr OpPC) {
13441345
const Pointer &Ptr = S.Stk.peek<Pointer>();
1345-
13461346
if (Ptr.canBeInitialized())
13471347
Ptr.initialize();
1348+
Ptr.activate();
13481349
return true;
13491350
}
13501351

clang/test/AST/Interp/unions.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,15 @@ constexpr U1 u1{};
1818
static_assert(u1.f == 3.0, "");
1919
static_assert(u1.i == 1, ""); // both-error {{not an integral constant expression}} \
2020
// both-note {{read of member 'i' of union with active member 'f'}}
21+
22+
23+
24+
union A {
25+
int a;
26+
double d;
27+
};
28+
constexpr A aa = {1, 2.0}; // both-error {{excess elements in union initializer}}
29+
constexpr A ab = {.d = 1.0};
30+
static_assert(ab.d == 1.0, "");
31+
static_assert(ab.a == 1, ""); // both-error {{not an integral constant expression}} \
32+
// both-note {{read of member 'a' of union with active member 'd'}}

0 commit comments

Comments
 (0)