Skip to content

Commit db3dcdc

Browse files
committed
[clang][Interp] Fix initializing base class members
For the given test case, we were trying to initialize a member of C, which doesn't have any. Get the proper base pointer instead and initialize the members there. Differential Revision: https://reviews.llvm.org/D143466
1 parent c0dbe85 commit db3dcdc

File tree

6 files changed

+109
-14
lines changed

6 files changed

+109
-14
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,12 +1370,12 @@ bool ByteCodeExprGen<Emitter>::visitRecordInitializer(const Expr *Initializer) {
13701370

13711371
unsigned InitIndex = 0;
13721372
for (const Expr *Init : InitList->inits()) {
1373-
const Record::Field *FieldToInit = R->getField(InitIndex);
13741373

13751374
if (!this->emitDupPtr(Initializer))
13761375
return false;
13771376

13781377
if (std::optional<PrimType> T = classify(Init)) {
1378+
const Record::Field *FieldToInit = R->getField(InitIndex);
13791379
if (!this->visit(Init))
13801380
return false;
13811381

@@ -1385,16 +1385,29 @@ bool ByteCodeExprGen<Emitter>::visitRecordInitializer(const Expr *Initializer) {
13851385
if (!this->emitPopPtr(Initializer))
13861386
return false;
13871387
} else {
1388-
// Non-primitive case. Get a pointer to the field-to-initialize
1389-
// on the stack and recurse into visitInitializer().
1390-
if (!this->emitGetPtrField(FieldToInit->Offset, Init))
1391-
return false;
1388+
// Initializer for a direct base class.
1389+
if (const Record::Base *B = R->getBase(Init->getType())) {
1390+
if (!this->emitGetPtrBasePop(B->Offset, Init))
1391+
return false;
13921392

1393-
if (!this->visitInitializer(Init))
1394-
return false;
1393+
if (!this->visitInitializer(Init))
1394+
return false;
13951395

1396-
if (!this->emitPopPtr(Initializer))
1397-
return false;
1396+
if (!this->emitPopPtr(Initializer))
1397+
return false;
1398+
} else {
1399+
const Record::Field *FieldToInit = R->getField(InitIndex);
1400+
// Non-primitive case. Get a pointer to the field-to-initialize
1401+
// on the stack and recurse into visitInitializer().
1402+
if (!this->emitGetPtrField(FieldToInit->Offset, Init))
1403+
return false;
1404+
1405+
if (!this->visitInitializer(Init))
1406+
return false;
1407+
1408+
if (!this->emitPopPtr(Initializer))
1409+
return false;
1410+
}
13981411
}
13991412
++InitIndex;
14001413
}

clang/lib/AST/Interp/Interp.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
434434

435435
// Check Fields in all bases
436436
for (const Record::Base &B : R->bases()) {
437-
Pointer P = Pointer(BasePtr.block(), B.Offset);
437+
Pointer P = BasePtr.atField(B.Offset);
438438
Result &= CheckFieldsInitialized(S, OpPC, P, B.R);
439439
}
440440

clang/lib/AST/Interp/Record.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ const Record::Base *Record::getBase(const RecordDecl *FD) const {
3939
return It->second;
4040
}
4141

42+
const Record::Base *Record::getBase(QualType T) const {
43+
if (!T->isRecordType())
44+
return nullptr;
45+
46+
const RecordDecl *RD = T->getAs<RecordType>()->getDecl();
47+
if (auto It = BaseMap.find(RD); It != BaseMap.end())
48+
return It->second;
49+
return nullptr;
50+
}
51+
4252
const Record::Base *Record::getVirtualBase(const RecordDecl *FD) const {
4353
auto It = VirtualBaseMap.find(FD);
4454
assert(It != VirtualBaseMap.end() && "Missing virtual base");

clang/lib/AST/Interp/Record.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ class Record final {
6161
const Field *getField(const FieldDecl *FD) const;
6262
/// Returns a base descriptor.
6363
const Base *getBase(const RecordDecl *FD) const;
64+
/// Returns a base descriptor.
65+
const Base *getBase(QualType T) const;
6466
/// Returns a virtual base descriptor.
6567
const Base *getVirtualBase(const RecordDecl *RD) const;
6668
// Returns the destructor of the record, if any.

clang/test/AST/Interp/cxx20.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,69 @@ namespace ConstThis {
272272
// ref-note {{in call to}}
273273
};
274274

275+
namespace BaseInit {
276+
struct Base {
277+
int a;
278+
};
279+
280+
struct Intermediate : Base {
281+
int b;
282+
};
283+
284+
struct Final : Intermediate {
285+
int c;
286+
287+
constexpr Final(int a, int b, int c) : c(c) {}
288+
};
289+
290+
static_assert(Final{1, 2, 3}.c == 3, ""); // OK
291+
static_assert(Final{1, 2, 3}.a == 0, ""); // expected-error {{not an integral constant expression}} \
292+
// expected-note {{read of object outside its lifetime}} \
293+
// ref-error {{not an integral constant expression}} \
294+
// ref-note {{read of uninitialized object}}
295+
296+
297+
struct Mixin {
298+
int b;
299+
300+
constexpr Mixin() = default;
301+
constexpr Mixin(int b) : b(b) {}
302+
};
303+
304+
struct Final2 : Base, Mixin {
305+
int c;
306+
307+
constexpr Final2(int a, int b, int c) : Mixin(b), c(c) {}
308+
constexpr Final2(int a, int b, int c, bool) : c(c) {}
309+
};
310+
311+
static_assert(Final2{1, 2, 3}.c == 3, ""); // OK
312+
static_assert(Final2{1, 2, 3}.b == 2, ""); // OK
313+
static_assert(Final2{1, 2, 3}.a == 0, ""); // expected-error {{not an integral constant expression}} \
314+
// expected-note {{read of object outside its lifetime}} \
315+
// ref-error {{not an integral constant expression}} \
316+
// ref-note {{read of uninitialized object}}
317+
318+
319+
struct Mixin3 {
320+
int b;
321+
};
322+
323+
struct Final3 : Base, Mixin3 {
324+
int c;
325+
326+
constexpr Final3(int a, int b, int c) : c(c) { this->b = b; }
327+
constexpr Final3(int a, int b, int c, bool) : c(c) {}
328+
};
329+
330+
static_assert(Final3{1, 2, 3}.c == 3, ""); // OK
331+
static_assert(Final3{1, 2, 3}.b == 2, ""); // OK
332+
static_assert(Final3{1, 2, 3}.a == 0, ""); // expected-error {{not an integral constant expression}} \
333+
// expected-note {{read of object outside its lifetime}} \
334+
// ref-error {{not an integral constant expression}} \
335+
// ref-note {{read of uninitialized object}}
336+
};
337+
275338
namespace Destructors {
276339

277340
class Inc final {

clang/test/AST/Interp/records.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,16 +252,23 @@ static_assert(s.m() == 1, "");
252252

253253
#if __cplusplus >= 201703L
254254
namespace BaseInit {
255+
class _A {public: int a;};
256+
class _B : public _A {};
257+
class _C : public _B {};
258+
259+
constexpr _C c{12};
260+
constexpr const _B &b = c;
261+
static_assert(b.a == 12);
262+
255263
class A {public: int a;};
256264
class B : public A {};
257265
class C : public A {};
258266
class D : public B, public C {};
259267

260-
// FIXME: Enable this once we support the initialization.
261268
// This initializes D::B::A::a and not D::C::A::a.
262-
//constexpr D d{12};
263-
//static_assert(d.B::a == 12);
264-
//static_assert(d.C::a == 0);
269+
constexpr D d{12};
270+
static_assert(d.B::a == 12);
271+
static_assert(d.C::a == 0);
265272
};
266273
#endif
267274

0 commit comments

Comments
 (0)