Author: Timm Bäder Date: 2023-01-19T09:42:22+01:00 New Revision: 6b0cd497b0fb584bdc0650b585c5bd1ed9edfc99
URL: https://github.com/llvm/llvm-project/commit/6b0cd497b0fb584bdc0650b585c5bd1ed9edfc99 DIFF: https://github.com/llvm/llvm-project/commit/6b0cd497b0fb584bdc0650b585c5bd1ed9edfc99.diff LOG: [clang][Interp] Check Field initialization after constructor call Differential Revision: https://reviews.llvm.org/D136694 Added: Modified: 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 Removed: ################################################################################ diff --git a/clang/lib/AST/Interp/Descriptor.cpp b/clang/lib/AST/Interp/Descriptor.cpp index 5575fc1e2a6e7..58158cd49cebc 100644 --- a/clang/lib/AST/Interp/Descriptor.cpp +++ b/clang/lib/AST/Interp/Descriptor.cpp @@ -126,7 +126,7 @@ static void ctorRecord(Block *B, char *Ptr, bool IsConst, bool IsMutable, 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; diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp index 940e71df138d3..d493a50486b7a 100644 --- a/clang/lib/AST/Interp/Interp.cpp +++ b/clang/lib/AST/Interp/Interp.cpp @@ -401,6 +401,38 @@ bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) { return false; } +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 Interpret(InterpState &S, APValue &Result) { // The current stack frame when we started Interpret(). // This is being used by the ops to determine wheter diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 07515548a06ba..65a49f21c5dc2 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -91,6 +91,9 @@ bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This); /// Checks if a method is pure virtual. bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD); +/// 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) { @@ -1256,8 +1259,10 @@ inline bool ExpandPtr(InterpState &S, CodePtr OpPC) { inline bool Call(InterpState &S, CodePtr &PC, const Function *Func) { auto NewFrame = std::make_unique<InterpFrame>(S, Func, PC); + Pointer ThisPtr; if (Func->hasThisPointer()) { - if (!CheckInvoke(S, PC, NewFrame->getThis())) { + ThisPtr = NewFrame->getThis(); + if (!CheckInvoke(S, PC, ThisPtr)) { return false; } } @@ -1275,6 +1280,11 @@ inline bool Call(InterpState &S, CodePtr &PC, const Function *Func) { 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() && !CheckCtorCall(S, PC, ThisPtr)) + return false; + return true; } diff --git a/clang/lib/AST/Interp/InterpFrame.cpp b/clang/lib/AST/Interp/InterpFrame.cpp index da228d011f874..19ac0bd5433a5 100644 --- a/clang/lib/AST/Interp/InterpFrame.cpp +++ b/clang/lib/AST/Interp/InterpFrame.cpp @@ -64,8 +64,6 @@ InterpFrame::InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC) } InterpFrame::~InterpFrame() { - if (Func && Func->isConstructor() && This.isBaseClass()) - This.initialize(); for (auto &Param : Params) S.deallocate(reinterpret_cast<Block *>(Param.second.get())); } diff --git a/clang/test/AST/Interp/cxx20.cpp b/clang/test/AST/Interp/cxx20.cpp index 5ec3e364b7ad4..de15cc9c259e6 100644 --- a/clang/test/AST/Interp/cxx20.cpp +++ b/clang/test/AST/Interp/cxx20.cpp @@ -85,11 +85,10 @@ static_assert(initializedLocal2() == 20); // expected-error {{not an integral co // ref-note {{in call to}} -struct Int { int a; }; +struct Int { int a; }; // expected-note {{subobject declared here}} constexpr int initializedLocal3() { - Int i; - return i.a; // expected-note {{read of object outside its lifetime}} \ - // ref-note {{read of uninitialized object is not allowed in a constant expression}} + Int i; // expected-note {{subobject of type 'int' is not initialized}} + return i.a; // ref-note {{read of uninitialized object is not allowed in a constant expression}} } static_assert(initializedLocal3() == 20); // expected-error {{not an integral constant expression}} \ // expected-note {{in call to}} \ @@ -134,3 +133,35 @@ constexpr auto b4 = name1() == name2(); // ref-error {{must be initialized by a // ref-note {{declared here}} static_assert(!b4); // ref-error {{not an integral constant expression}} \ // ref-note {{not a constant expression}} + +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}} +}; diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp index d556ad064bd64..afd6b7527337b 100644 --- a/clang/test/AST/Interp/records.cpp +++ b/clang/test/AST/Interp/records.cpp @@ -295,13 +295,14 @@ namespace DeriveFailures { // ref-note 2{{non-constexpr constructor 'Base' cannot be used in a constant expression}} }; - // FIXME: This is currently not being diagnosed with the new constant interpreter. constexpr Derived D(12); // ref-error {{must be initialized by a constant expression}} \ // ref-note {{in call to 'Derived(12)'}} \ // 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; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits