Skip to content

Commit af4c29c

Browse files
committed
[clang][Interp] IndirectMember initializers
1 parent e64e478 commit af4c29c

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
@@ -1065,15 +1065,18 @@ bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
10651065
return true;
10661066
}
10671067

1068+
// FIXME: The Field pointer here is too much IMO and we could instead just
1069+
// pass an Offset + BitWidth pair.
10681070
template <PrimType Name, class T = typename PrimConv<Name>::T>
1069-
bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
1071+
bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F,
1072+
uint32_t FieldOffset) {
10701073
assert(F->isBitField());
10711074
if (S.checkingPotentialConstantExpression())
10721075
return false;
10731076
const Pointer &This = S.Current->getThis();
10741077
if (!CheckThis(S, OpPC, This))
10751078
return false;
1076-
const Pointer &Field = This.atField(F->Offset);
1079+
const Pointer &Field = This.atField(FieldOffset);
10771080
const auto &Value = S.Stk.pop<T>();
10781081
Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx()));
10791082
Field.initialize();

clang/lib/AST/Interp/Opcodes.td

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

clang/test/AST/Interp/records.cpp

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

0 commit comments

Comments
 (0)