tbaeder created this revision. tbaeder added reviewers: aaron.ballman, erichkeane, tahonermann, shafik. Herald added a project: All. tbaeder requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
We need to do this, even for global variables (it seems). The diagnostic output is slightly different than what we get from the current interpreter, but I'm not sure if it's worse or not or if that needs to be changed. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D136694 Files: clang/lib/AST/Interp/Descriptor.cpp clang/lib/AST/Interp/Interp.cpp clang/lib/AST/Interp/Interp.h clang/lib/AST/Interp/InterpFrame.cpp clang/test/AST/Interp/cxx20.cpp clang/test/AST/Interp/records.cpp
Index: clang/test/AST/Interp/records.cpp =================================================================== --- clang/test/AST/Interp/records.cpp +++ clang/test/AST/Interp/records.cpp @@ -279,7 +279,9 @@ // ref-note {{declared here}} \ // expected-error {{must be initialized by a constant expression}} static_assert(D.Val == 0, ""); // ref-error {{not an integral constant expression}} \ - // ref-note {{initializer of 'D' is not a constant expression}} + // ref-note {{initializer of 'D' is not a constant expression}} \ + // expected-error {{not an integral constant expression}} \ + // expected-note {{read of object outside its lifetime}} struct AnotherBase { int Val; Index: clang/test/AST/Interp/cxx20.cpp =================================================================== --- clang/test/AST/Interp/cxx20.cpp +++ clang/test/AST/Interp/cxx20.cpp @@ -80,3 +80,42 @@ } static_assert(f()); #endif + + +namespace UninitializedFields { + class A { + public: + int a; // expected-note {{subobject declared here}} \ + // ref-note {{subobject declared here}} + constexpr A() {} + }; + constexpr A a; // 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 Base { + public: + bool b; + int a; // expected-note {{subobject declared here}} \ + // ref-note {{subobject declared here}} + constexpr Base() : b(true) {} + }; + + class Derived : public Base { + public: + constexpr Derived() : Base() {} // expected-note {{subobject of type 'int' is not initialized}} + }; + +constexpr Derived D; // expected-error {{must be initialized by a constant expression}} \\ + // expected-note {{in call to 'Derived()'}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{subobject of type 'int' is not initialized}} + + + + + + +}; Index: clang/lib/AST/Interp/InterpFrame.cpp =================================================================== --- clang/lib/AST/Interp/InterpFrame.cpp +++ clang/lib/AST/Interp/InterpFrame.cpp @@ -61,8 +61,6 @@ } InterpFrame::~InterpFrame() { - if (Func && Func->isConstructor() && This.isBaseClass()) - This.initialize(); for (auto &Param : Params) S.deallocate(reinterpret_cast<Block *>(Param.second.get())); } Index: clang/lib/AST/Interp/Interp.h =================================================================== --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -96,6 +96,9 @@ bool CheckFloatResult(InterpState &S, CodePtr OpPC, APFloat::opStatus Status, const Floating &Result); +/// Checks that all fields are initialized after a constructor call. +bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This); + /// Checks if Div/Rem operation on LHS and RHS is valid. template <typename T> bool CheckDivRem(InterpState &S, CodePtr OpPC, const T &LHS, const T &RHS) { @@ -1338,7 +1341,9 @@ inline bool Call(InterpState &S, CodePtr &PC, const Function *Func) { auto NewFrame = std::make_unique<InterpFrame>(S, Func, PC); + Pointer ThisPtr; if (Func->hasThisPointer()) { + ThisPtr = NewFrame->getThis(); if (!CheckInvoke(S, PC, NewFrame->getThis())) { return false; } @@ -1355,6 +1360,13 @@ if (Interpret(S, CallResult)) { NewFrame.release(); // Frame was delete'd already. assert(S.Current == FrameBefore); + + // For constructors, check that all fields have been initialized. + if (Func->isConstructor()) { + if (!CheckCtorCall(S, PC, ThisPtr)) + return false; + } + return true; } Index: clang/lib/AST/Interp/Interp.cpp =================================================================== --- clang/lib/AST/Interp/Interp.cpp +++ clang/lib/AST/Interp/Interp.cpp @@ -435,6 +435,38 @@ return true; } +static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC, + const Pointer &BasePtr, const Record *R) { + assert(R); + bool Result = true; + // 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(); + + 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. + } 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); + Result = false; + } + } + return Result; +} + +bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This) { + assert(!This.isZero()); + const Record *R = This.getRecord(); + return CheckFieldsInitialized(S, OpPC, This, R); +} + bool CastFP(InterpState &S, CodePtr OpPC, const llvm::fltSemantics *Sem, llvm::RoundingMode RM) { Floating F = S.Stk.pop<Floating>(); Index: clang/lib/AST/Interp/Descriptor.cpp =================================================================== --- clang/lib/AST/Interp/Descriptor.cpp +++ clang/lib/AST/Interp/Descriptor.cpp @@ -121,7 +121,7 @@ auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + SubOff) - 1; Desc->Offset = SubOff; Desc->Desc = F; - Desc->IsInitialized = (B->isStatic() || F->IsArray) && !IsBase; + Desc->IsInitialized = F->IsArray && !IsBase; Desc->IsBase = IsBase; Desc->IsActive = IsActive && !IsUnion; Desc->IsConst = IsConst || F->IsConst;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits