[clang] 6b0cd49 - [clang][Interp] Check Field initialization after constructor call

Timm Bäder via cfe-commits cfe-commits at lists.llvm.org
Thu Jan 19 00:42:33 PST 2023


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;


        


More information about the cfe-commits mailing list