[clang] 9308014 - [clang][Interp] Diagnose uninitialized array record fields

Timm Bäder via cfe-commits cfe-commits at lists.llvm.org
Thu Jan 19 02:08:08 PST 2023


Author: Timm Bäder
Date: 2023-01-19T11:07:51+01:00
New Revision: 9308014195e2f091c0025ef4aa26ece080b7da8d

URL: https://github.com/llvm/llvm-project/commit/9308014195e2f091c0025ef4aa26ece080b7da8d
DIFF: https://github.com/llvm/llvm-project/commit/9308014195e2f091c0025ef4aa26ece080b7da8d.diff

LOG: [clang][Interp] Diagnose uninitialized array record fields

Just like we do for record members, diagnose uninitialized array record
fields.

Differential Revision: https://reviews.llvm.org/D136828

Added: 
    

Modified: 
    clang/lib/AST/Interp/Interp.cpp
    clang/lib/AST/Interp/Pointer.h
    clang/test/AST/Interp/cxx20.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index d493a50486b7..6a600b306bad 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -401,6 +401,48 @@ bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) {
   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<RecordType>(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<ConstantArrayType>(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);
@@ -408,19 +450,17 @@ static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
   // 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<ConstantArrayType>(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;
     }
   }

diff  --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h
index c0628314e869..ce113a54e122 100644
--- a/clang/lib/AST/Interp/Pointer.h
+++ b/clang/lib/AST/Interp/Pointer.h
@@ -242,6 +242,8 @@ class Pointer {
 
   /// 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(); }
 

diff  --git a/clang/test/AST/Interp/cxx20.cpp b/clang/test/AST/Interp/cxx20.cpp
index de15cc9c259e..e9505b3cf718 100644
--- a/clang/test/AST/Interp/cxx20.cpp
+++ b/clang/test/AST/Interp/cxx20.cpp
@@ -137,8 +137,8 @@ static_assert(!b4); // ref-error {{not an integral constant expression}} \
 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}} \
@@ -164,4 +164,40 @@ constexpr Derived D; // expected-error {{must be initialized by a constant expre
                      // 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
 };


        


More information about the cfe-commits mailing list