Skip to content

Commit 58ff884

Browse files
committed
[clang][Interp] IndirectMember initializers
1 parent 12e425d commit 58ff884

File tree

4 files changed

+114
-26
lines changed

4 files changed

+114
-26
lines changed

clang/lib/AST/Interp/ByteCodeStmtGen.cpp

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,27 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
142142
// Classify the return type.
143143
ReturnType = this->classify(F->getReturnType());
144144

145+
auto emitFieldInitializer = [&](const Record::Field *F, unsigned FieldOffset,
146+
const Expr *InitExpr) -> bool {
147+
if (std::optional<PrimType> T = this->classify(InitExpr)) {
148+
if (!this->visit(InitExpr))
149+
return false;
150+
151+
if (F->isBitField())
152+
return this->emitInitThisBitField(*T, F, FieldOffset, InitExpr);
153+
return this->emitInitThisField(*T, FieldOffset, InitExpr);
154+
}
155+
// Non-primitive case. Get a pointer to the field-to-initialize
156+
// on the stack and call visitInitialzer() for it.
157+
if (!this->emitGetPtrThisField(FieldOffset, InitExpr))
158+
return false;
159+
160+
if (!this->visitInitializer(InitExpr))
161+
return false;
162+
163+
return this->emitPopPtr(InitExpr);
164+
};
165+
145166
// Emit custom code if this is a lambda static invoker.
146167
if (const auto *MD = dyn_cast<CXXMethodDecl>(F);
147168
MD && MD->isLambdaStaticInvoker())
@@ -162,29 +183,8 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
162183
if (const FieldDecl *Member = Init->getMember()) {
163184
const Record::Field *F = R->getField(Member);
164185

165-
if (std::optional<PrimType> T = this->classify(InitExpr)) {
166-
if (!this->visit(InitExpr))
167-
return false;
168-
169-
if (F->isBitField()) {
170-
if (!this->emitInitThisBitField(*T, F, InitExpr))
171-
return false;
172-
} else {
173-
if (!this->emitInitThisField(*T, F->Offset, InitExpr))
174-
return false;
175-
}
176-
} else {
177-
// Non-primitive case. Get a pointer to the field-to-initialize
178-
// on the stack and call visitInitialzer() for it.
179-
if (!this->emitGetPtrThisField(F->Offset, InitExpr))
180-
return false;
181-
182-
if (!this->visitInitializer(InitExpr))
183-
return false;
184-
185-
if (!this->emitPopPtr(InitExpr))
186-
return false;
187-
}
186+
if (!emitFieldInitializer(F, F->Offset, InitExpr))
187+
return false;
188188
} else if (const Type *Base = Init->getBaseClass()) {
189189
// Base class initializer.
190190
// Get This Base and call initializer on it.
@@ -198,6 +198,27 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
198198
return false;
199199
if (!this->emitInitPtrPop(InitExpr))
200200
return false;
201+
} else if (const IndirectFieldDecl *IFD = Init->getIndirectMember()) {
202+
assert(IFD->getChainingSize() >= 2);
203+
204+
unsigned NestedFieldOffset = 0;
205+
const Record::Field *NestedField = nullptr;
206+
for (const NamedDecl *ND : IFD->chain()) {
207+
// FIXME: Can this *not* be a FieldDecl?
208+
const FieldDecl *FD = cast<FieldDecl>(ND);
209+
const Record *FieldRecord =
210+
this->P.getOrCreateRecord(FD->getParent());
211+
assert(FieldRecord);
212+
213+
NestedField = FieldRecord->getField(FD);
214+
assert(NestedField);
215+
216+
NestedFieldOffset += NestedField->Offset;
217+
}
218+
assert(NestedField);
219+
220+
if (!emitFieldInitializer(NestedField, NestedFieldOffset, InitExpr))
221+
return false;
201222
} else {
202223
assert(Init->isDelegatingInitializer());
203224
if (!this->emitThis(InitExpr))

clang/lib/AST/Interp/Interp.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,15 +1078,18 @@ bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
10781078
return true;
10791079
}
10801080

1081+
// FIXME: The Field pointer here is too much IMO and we could instead just
1082+
// pass an Offset + BitWidth pair.
10811083
template <PrimType Name, class T = typename PrimConv<Name>::T>
1082-
bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
1084+
bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F,
1085+
uint32_t FieldOffset) {
10831086
assert(F->isBitField());
10841087
if (S.checkingPotentialConstantExpression())
10851088
return false;
10861089
const Pointer &This = S.Current->getThis();
10871090
if (!CheckThis(S, OpPC, This))
10881091
return false;
1089-
const Pointer &Field = This.atField(F->Offset);
1092+
const Pointer &Field = This.atField(FieldOffset);
10901093
const auto &Value = S.Stk.pop<T>();
10911094
Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx()));
10921095
Field.initialize();

clang/lib/AST/Interp/Opcodes.td

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,11 @@ def InitThisField : AccessOpcode;
417417
// [Value] -> []
418418
def InitThisFieldActive : AccessOpcode;
419419
// [Value] -> []
420-
def InitThisBitField : BitFieldOpcode;
420+
def InitThisBitField : Opcode {
421+
let Types = [AluTypeClass];
422+
let Args = [ArgRecordField, ArgUint32];
423+
let HasGroup = 1;
424+
}
421425
// [Pointer, Value] -> []
422426
def InitField : AccessOpcode;
423427
// [Pointer, Value] -> []

clang/test/AST/Interp/records.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,3 +1133,63 @@ namespace AccessOnNullptr {
11331133
// ref-error {{not an integral constant expression}} \
11341134
// ref-note {{in call to 'a2()'}}
11351135
}
1136+
1137+
namespace IndirectFieldInit {
1138+
#if __cplusplus >= 202002L
1139+
/// Primitive.
1140+
struct Nested1 {
1141+
struct {
1142+
int first;
1143+
};
1144+
int x;
1145+
constexpr Nested1(int x) : first(12), x() { x = 4; }
1146+
constexpr Nested1() : Nested1(42) {}
1147+
};
1148+
constexpr Nested1 N1{};
1149+
static_assert(N1.first == 12, "");
1150+
1151+
/// Composite.
1152+
struct Nested2 {
1153+
struct First { int x = 42; };
1154+
struct {
1155+
First first;
1156+
};
1157+
int x;
1158+
constexpr Nested2(int x) : first(12), x() { x = 4; }
1159+
constexpr Nested2() : Nested2(42) {}
1160+
};
1161+
constexpr Nested2 N2{};
1162+
static_assert(N2.first.x == 12, "");
1163+
1164+
/// Bitfield.
1165+
struct Nested3 {
1166+
struct {
1167+
unsigned first : 2;
1168+
};
1169+
int x;
1170+
constexpr Nested3(int x) : first(3), x() { x = 4; }
1171+
constexpr Nested3() : Nested3(42) {}
1172+
};
1173+
1174+
constexpr Nested3 N3{};
1175+
static_assert(N3.first == 3, "");
1176+
1177+
/// Test that we get the offset right if the
1178+
/// record has a base.
1179+
struct Nested4Base {
1180+
int a;
1181+
int b;
1182+
char c;
1183+
};
1184+
struct Nested4 : Nested4Base{
1185+
struct {
1186+
int first;
1187+
};
1188+
int x;
1189+
constexpr Nested4(int x) : first(123), x() { a = 1; b = 2; c = 3; x = 4; }
1190+
constexpr Nested4() : Nested4(42) {}
1191+
};
1192+
constexpr Nested4 N4{};
1193+
static_assert(N4.first == 123, "");
1194+
#endif
1195+
}

0 commit comments

Comments
 (0)