Skip to content

Commit

Permalink
[clang][Interp] Fix initializing base class members
Browse files Browse the repository at this point in the history
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
  • Loading branch information
tbaederr committed Apr 3, 2023
1 parent c0dbe85 commit db3dcdc
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 14 deletions.
31 changes: 22 additions & 9 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Expand Up @@ -1370,12 +1370,12 @@ bool ByteCodeExprGen<Emitter>::visitRecordInitializer(const Expr *Initializer) {

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

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

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

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

if (!this->visitInitializer(Init))
return false;
if (!this->visitInitializer(Init))
return false;

if (!this->emitPopPtr(Initializer))
return false;
if (!this->emitPopPtr(Initializer))
return false;
} else {
const Record::Field *FieldToInit = R->getField(InitIndex);
// Non-primitive case. Get a pointer to the field-to-initialize
// on the stack and recurse into visitInitializer().
if (!this->emitGetPtrField(FieldToInit->Offset, Init))
return false;

if (!this->visitInitializer(Init))
return false;

if (!this->emitPopPtr(Initializer))
return false;
}
}
++InitIndex;
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Interp/Interp.cpp
Expand Up @@ -434,7 +434,7 @@ static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,

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

Expand Down
10 changes: 10 additions & 0 deletions clang/lib/AST/Interp/Record.cpp
Expand Up @@ -39,6 +39,16 @@ const Record::Base *Record::getBase(const RecordDecl *FD) const {
return It->second;
}

const Record::Base *Record::getBase(QualType T) const {
if (!T->isRecordType())
return nullptr;

const RecordDecl *RD = T->getAs<RecordType>()->getDecl();
if (auto It = BaseMap.find(RD); It != BaseMap.end())
return It->second;
return nullptr;
}

const Record::Base *Record::getVirtualBase(const RecordDecl *FD) const {
auto It = VirtualBaseMap.find(FD);
assert(It != VirtualBaseMap.end() && "Missing virtual base");
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/Interp/Record.h
Expand Up @@ -61,6 +61,8 @@ class Record final {
const Field *getField(const FieldDecl *FD) const;
/// Returns a base descriptor.
const Base *getBase(const RecordDecl *FD) const;
/// Returns a base descriptor.
const Base *getBase(QualType T) const;
/// Returns a virtual base descriptor.
const Base *getVirtualBase(const RecordDecl *RD) const;
// Returns the destructor of the record, if any.
Expand Down
63 changes: 63 additions & 0 deletions clang/test/AST/Interp/cxx20.cpp
Expand Up @@ -272,6 +272,69 @@ namespace ConstThis {
// ref-note {{in call to}}
};

namespace BaseInit {
struct Base {
int a;
};

struct Intermediate : Base {
int b;
};

struct Final : Intermediate {
int c;

constexpr Final(int a, int b, int c) : c(c) {}
};

static_assert(Final{1, 2, 3}.c == 3, ""); // OK
static_assert(Final{1, 2, 3}.a == 0, ""); // expected-error {{not an integral constant expression}} \
// expected-note {{read of object outside its lifetime}} \
// ref-error {{not an integral constant expression}} \
// ref-note {{read of uninitialized object}}


struct Mixin {
int b;

constexpr Mixin() = default;
constexpr Mixin(int b) : b(b) {}
};

struct Final2 : Base, Mixin {
int c;

constexpr Final2(int a, int b, int c) : Mixin(b), c(c) {}
constexpr Final2(int a, int b, int c, bool) : c(c) {}
};

static_assert(Final2{1, 2, 3}.c == 3, ""); // OK
static_assert(Final2{1, 2, 3}.b == 2, ""); // OK
static_assert(Final2{1, 2, 3}.a == 0, ""); // expected-error {{not an integral constant expression}} \
// expected-note {{read of object outside its lifetime}} \
// ref-error {{not an integral constant expression}} \
// ref-note {{read of uninitialized object}}


struct Mixin3 {
int b;
};

struct Final3 : Base, Mixin3 {
int c;

constexpr Final3(int a, int b, int c) : c(c) { this->b = b; }
constexpr Final3(int a, int b, int c, bool) : c(c) {}
};

static_assert(Final3{1, 2, 3}.c == 3, ""); // OK
static_assert(Final3{1, 2, 3}.b == 2, ""); // OK
static_assert(Final3{1, 2, 3}.a == 0, ""); // expected-error {{not an integral constant expression}} \
// expected-note {{read of object outside its lifetime}} \
// ref-error {{not an integral constant expression}} \
// ref-note {{read of uninitialized object}}
};

namespace Destructors {

class Inc final {
Expand Down
15 changes: 11 additions & 4 deletions clang/test/AST/Interp/records.cpp
Expand Up @@ -252,16 +252,23 @@ static_assert(s.m() == 1, "");

#if __cplusplus >= 201703L
namespace BaseInit {
class _A {public: int a;};
class _B : public _A {};
class _C : public _B {};

constexpr _C c{12};
constexpr const _B &b = c;
static_assert(b.a == 12);

class A {public: int a;};
class B : public A {};
class C : public A {};
class D : public B, public C {};

// FIXME: Enable this once we support the initialization.
// This initializes D::B::A::a and not D::C::A::a.
//constexpr D d{12};
//static_assert(d.B::a == 12);
//static_assert(d.C::a == 0);
constexpr D d{12};
static_assert(d.B::a == 12);
static_assert(d.C::a == 0);
};
#endif

Expand Down

0 comments on commit db3dcdc

Please sign in to comment.