Index: clang/lib/AST/Interp/Interp.cpp =================================================================== --- clang/lib/AST/Interp/Interp.cpp +++ clang/lib/AST/Interp/Interp.cpp @@ -407,6 +407,48 @@ return false; } +static void DiagnoseUninitializedSubobject(InterpState &S, const SourceInfo &SI, + QualType SubObjType, + SourceLocation SubObjLoc) { + S.FFDiag(SI, diag::note_constexpr_uninitialized) << true << SubObjType; + if (SubObjLoc.isValid()) + S.Note(SubObjLoc, diag::note_constexpr_subobject_declared_here); +} + +static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC, + const Pointer &BasePtr, const Record *R); + +static bool CheckArrayInitialized(InterpState &S, CodePtr OpPC, + const Pointer &BasePtr, + const ConstantArrayType *CAT) { + bool Result = true; + size_t NumElems = CAT->getSize().getZExtValue(); + QualType ElemType = CAT->getElementType(); + + if (isa(ElemType.getTypePtr())) { + const Record *R = BasePtr.getElemRecord(); + for (size_t I = 0; I != NumElems; ++I) { + Pointer ElemPtr = BasePtr.atIndex(I).narrow(); + Result &= CheckFieldsInitialized(S, OpPC, ElemPtr, R); + } + } else if (auto *ElemCAT = dyn_cast(ElemType)) { + for (size_t I = 0; I != NumElems; ++I) { + Pointer ElemPtr = BasePtr.atIndex(I).narrow(); + Result &= CheckArrayInitialized(S, OpPC, ElemPtr, ElemCAT); + } + } else { + for (size_t I = 0; I != NumElems; ++I) { + if (!BasePtr.atIndex(I).isInitialized()) { + DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), ElemType, + BasePtr.getFieldDesc()->getLocation()); + Result = false; + } + } + } + + return Result; +} + static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC, const Pointer &BasePtr, const Record *R) { assert(R); @@ -414,19 +456,16 @@ // Check all fields of this record are initialized. for (const Record::Field &F : R->fields()) { Pointer FieldPtr = BasePtr.atField(F.Offset); - QualType FieldType = FieldPtr.getType(); + QualType FieldType = F.Decl->getType(); if (FieldType->isRecordType()) { Result &= CheckFieldsInitialized(S, OpPC, FieldPtr, FieldPtr.getRecord()); } else if (FieldType->isArrayType()) { - // FIXME: Arrays need to be handled here as well I think. + const auto *CAT = cast(FieldType->getAsArrayTypeUnsafe()); + Result &= CheckArrayInitialized(S, OpPC, FieldPtr, CAT); } else if (!FieldPtr.isInitialized()) { - const SourceInfo &SI = S.Current->getSource(OpPC); - S.FFDiag(SI, diag::note_constexpr_uninitialized) - << true << F.Decl->getType(); - SourceLocation SubobjectLoc = F.Decl->getLocation(); - if (SubobjectLoc.isValid()) - S.Note(SubobjectLoc, diag::note_constexpr_subobject_declared_here); + DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), + F.Decl->getType(), F.Decl->getLocation()); Result = false; } } Index: clang/lib/AST/Interp/Pointer.h =================================================================== --- clang/lib/AST/Interp/Pointer.h +++ clang/lib/AST/Interp/Pointer.h @@ -242,6 +242,8 @@ /// Returns the record descriptor of a class. Record *getRecord() const { return getFieldDesc()->ElemRecord; } + // Returns the element record type, if this is a non-primive array. + Record *getElemRecord() const { return getFieldDesc()->ElemDesc->ElemRecord; } /// Returns the field information. const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); } Index: clang/test/AST/Interp/cxx20.cpp =================================================================== --- clang/test/AST/Interp/cxx20.cpp +++ clang/test/AST/Interp/cxx20.cpp @@ -85,8 +85,8 @@ namespace UninitializedFields { class A { public: - int a; // expected-note {{subobject declared here}} \ - // ref-note {{subobject declared here}} + int a; // expected-note 2{{subobject declared here}} \ + // ref-note 2{{subobject declared here}} constexpr A() {} }; constexpr A a; // expected-error {{must be initialized by a constant expression}} \ @@ -112,6 +112,42 @@ // expected-note {{in call to 'Derived()'}} \ // ref-error {{must be initialized by a constant expression}} \ // ref-note {{subobject of type 'int' is not initialized}} + + class C2 { + public: + A a; + constexpr C2() {} // expected-note {{subobject of type 'int' is not initialized}} + }; + constexpr C2 c2; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{in call to 'C2()'}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{subobject of type 'int' is not initialized}} + + + // FIXME: These two are currently disabled because the array fields + // cannot be initialized. +#if 0 + class C3 { + public: + A a[2]; + constexpr C3() {} + }; + constexpr C3 c3; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{subobject of type 'int' is not initialized}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{subobject of type 'int' is not initialized}} + + class C4 { + public: + bool B[2][3]; // expected-note {{subobject declared here}} \ + // ref-note {{subobject declared here}} + constexpr C4(){} + }; + constexpr C4 c4; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{subobject of type 'bool' is not initialized}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{subobject of type 'bool' is not initialized}} +#endif }; namespace ConstThis {