diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -1370,12 +1370,12 @@ 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 T = classify(Init)) { + const Record::Field *FieldToInit = R->getField(InitIndex); if (!this->visit(Init)) return false; @@ -1385,16 +1385,29 @@ 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; } diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -434,7 +434,7 @@ // 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); } diff --git a/clang/lib/AST/Interp/Record.h b/clang/lib/AST/Interp/Record.h --- a/clang/lib/AST/Interp/Record.h +++ b/clang/lib/AST/Interp/Record.h @@ -61,6 +61,8 @@ 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. diff --git a/clang/lib/AST/Interp/Record.cpp b/clang/lib/AST/Interp/Record.cpp --- a/clang/lib/AST/Interp/Record.cpp +++ b/clang/lib/AST/Interp/Record.cpp @@ -39,6 +39,16 @@ return It->second; } +const Record::Base *Record::getBase(QualType T) const { + if (!T->isRecordType()) + return nullptr; + + const RecordDecl *RD = T->getAs()->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"); diff --git a/clang/test/AST/Interp/cxx20.cpp b/clang/test/AST/Interp/cxx20.cpp --- a/clang/test/AST/Interp/cxx20.cpp +++ b/clang/test/AST/Interp/cxx20.cpp @@ -272,6 +272,69 @@ // 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 { diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp --- a/clang/test/AST/Interp/records.cpp +++ b/clang/test/AST/Interp/records.cpp @@ -252,16 +252,23 @@ #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