[clang] db3dcdc - [clang][Interp] Fix initializing base class members

Timm Bäder via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 3 04:51:20 PDT 2023


Author: Timm Bäder
Date: 2023-04-03T13:51:04+02:00
New Revision: db3dcdc08ce06e301cdcc75e2849315a47d7a28d

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

LOG: [clang][Interp] Fix initializing base class members

For the given test case, we were trying to initialize a member of C,
which doesn't have any. Get the proper base pointer instead and
initialize the members there.

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

Added: 
    

Modified: 
    clang/lib/AST/Interp/ByteCodeExprGen.cpp
    clang/lib/AST/Interp/Interp.cpp
    clang/lib/AST/Interp/Record.cpp
    clang/lib/AST/Interp/Record.h
    clang/test/AST/Interp/cxx20.cpp
    clang/test/AST/Interp/records.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index ad802f72aad4c..665f60d572ea7 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -1370,12 +1370,12 @@ bool ByteCodeExprGen<Emitter>::visitRecordInitializer(const Expr *Initializer) {
 
     unsigned InitIndex = 0;
     for (const Expr *Init : InitList->inits()) {
-      const Record::Field *FieldToInit = R->getField(InitIndex);
 
       if (!this->emitDupPtr(Initializer))
         return false;
 
       if (std::optional<PrimType> T = classify(Init)) {
+        const Record::Field *FieldToInit = R->getField(InitIndex);
         if (!this->visit(Init))
           return false;
 
@@ -1385,16 +1385,29 @@ bool ByteCodeExprGen<Emitter>::visitRecordInitializer(const Expr *Initializer) {
         if (!this->emitPopPtr(Initializer))
           return false;
       } else {
-        // Non-primitive case. Get a pointer to the field-to-initialize
-        // on the stack and recurse into visitInitializer().
-        if (!this->emitGetPtrField(FieldToInit->Offset, Init))
-          return false;
+        // Initializer for a direct base class.
+        if (const Record::Base *B = R->getBase(Init->getType())) {
+          if (!this->emitGetPtrBasePop(B->Offset, Init))
+            return false;
 
-        if (!this->visitInitializer(Init))
-          return false;
+          if (!this->visitInitializer(Init))
+            return false;
 
-        if (!this->emitPopPtr(Initializer))
-          return false;
+          if (!this->emitPopPtr(Initializer))
+            return false;
+        } else {
+          const Record::Field *FieldToInit = R->getField(InitIndex);
+          // Non-primitive case. Get a pointer to the field-to-initialize
+          // on the stack and recurse into visitInitializer().
+          if (!this->emitGetPtrField(FieldToInit->Offset, Init))
+            return false;
+
+          if (!this->visitInitializer(Init))
+            return false;
+
+          if (!this->emitPopPtr(Initializer))
+            return false;
+        }
       }
       ++InitIndex;
     }

diff  --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 554e9a8e2d204..3af8027ddeb53 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -434,7 +434,7 @@ static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
 
   // Check Fields in all bases
   for (const Record::Base &B : R->bases()) {
-    Pointer P = Pointer(BasePtr.block(), B.Offset);
+    Pointer P = BasePtr.atField(B.Offset);
     Result &= CheckFieldsInitialized(S, OpPC, P, B.R);
   }
 

diff  --git a/clang/lib/AST/Interp/Record.cpp b/clang/lib/AST/Interp/Record.cpp
index f440c4705051e..c8cbdb314f512 100644
--- a/clang/lib/AST/Interp/Record.cpp
+++ b/clang/lib/AST/Interp/Record.cpp
@@ -39,6 +39,16 @@ const Record::Base *Record::getBase(const RecordDecl *FD) const {
   return It->second;
 }
 
+const Record::Base *Record::getBase(QualType T) const {
+  if (!T->isRecordType())
+    return nullptr;
+
+  const RecordDecl *RD = T->getAs<RecordType>()->getDecl();
+  if (auto It = BaseMap.find(RD); It != BaseMap.end())
+    return It->second;
+  return nullptr;
+}
+
 const Record::Base *Record::getVirtualBase(const RecordDecl *FD) const {
   auto It = VirtualBaseMap.find(FD);
   assert(It != VirtualBaseMap.end() && "Missing virtual base");

diff  --git a/clang/lib/AST/Interp/Record.h b/clang/lib/AST/Interp/Record.h
index 1742cb1cc4ee6..52173fffa5447 100644
--- a/clang/lib/AST/Interp/Record.h
+++ b/clang/lib/AST/Interp/Record.h
@@ -61,6 +61,8 @@ class Record final {
   const Field *getField(const FieldDecl *FD) const;
   /// Returns a base descriptor.
   const Base *getBase(const RecordDecl *FD) const;
+  /// Returns a base descriptor.
+  const Base *getBase(QualType T) const;
   /// Returns a virtual base descriptor.
   const Base *getVirtualBase(const RecordDecl *RD) const;
   // Returns the destructor of the record, if any.

diff  --git a/clang/test/AST/Interp/cxx20.cpp b/clang/test/AST/Interp/cxx20.cpp
index 480d6e8e9a8a2..cfa9729f4b03d 100644
--- a/clang/test/AST/Interp/cxx20.cpp
+++ b/clang/test/AST/Interp/cxx20.cpp
@@ -272,6 +272,69 @@ namespace ConstThis {
                                        // ref-note {{in call to}}
 };
 
+namespace BaseInit {
+  struct Base {
+    int a;
+  };
+
+  struct Intermediate : Base {
+    int b;
+  };
+
+  struct Final : Intermediate {
+    int c;
+
+    constexpr Final(int a, int b, int c) : c(c) {}
+  };
+
+  static_assert(Final{1, 2, 3}.c == 3, ""); // OK
+  static_assert(Final{1, 2, 3}.a == 0, ""); // expected-error {{not an integral constant expression}} \
+                                            // expected-note {{read of object outside its lifetime}} \
+                                            // ref-error {{not an integral constant expression}} \
+                                            // ref-note {{read of uninitialized object}}
+
+
+  struct Mixin  {
+    int b;
+
+    constexpr Mixin() = default;
+    constexpr Mixin(int b) : b(b) {}
+  };
+
+  struct Final2 : Base, Mixin {
+    int c;
+
+    constexpr Final2(int a, int b, int c) : Mixin(b), c(c) {}
+    constexpr Final2(int a, int b, int c, bool) : c(c) {}
+  };
+
+  static_assert(Final2{1, 2, 3}.c == 3, ""); // OK
+  static_assert(Final2{1, 2, 3}.b == 2, ""); // OK
+  static_assert(Final2{1, 2, 3}.a == 0, ""); // expected-error {{not an integral constant expression}} \
+                                             // expected-note {{read of object outside its lifetime}} \
+                                             // ref-error {{not an integral constant expression}} \
+                                             // ref-note {{read of uninitialized object}}
+
+
+  struct Mixin3  {
+    int b;
+  };
+
+  struct Final3 : Base, Mixin3 {
+    int c;
+
+    constexpr Final3(int a, int b, int c) : c(c) { this->b = b; }
+    constexpr Final3(int a, int b, int c, bool) : c(c) {}
+  };
+
+  static_assert(Final3{1, 2, 3}.c == 3, ""); // OK
+  static_assert(Final3{1, 2, 3}.b == 2, ""); // OK
+  static_assert(Final3{1, 2, 3}.a == 0, ""); // expected-error {{not an integral constant expression}} \
+                                             // expected-note {{read of object outside its lifetime}} \
+                                             // ref-error {{not an integral constant expression}} \
+                                             // ref-note {{read of uninitialized object}}
+};
+
 namespace Destructors {
 
   class Inc final {

diff  --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index 188db827fe08b..37cdf341fbd70 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -252,16 +252,23 @@ static_assert(s.m() == 1, "");
 
 #if __cplusplus >= 201703L
 namespace BaseInit {
+  class _A {public: int a;};
+  class _B : public _A {};
+  class _C : public _B {};
+
+  constexpr _C c{12};
+  constexpr const _B &b = c;
+  static_assert(b.a == 12);
+
   class A {public: int a;};
   class B : public A {};
   class C : public A {};
   class D : public B, public C {};
 
-  // FIXME: Enable this once we support the initialization.
   // This initializes D::B::A::a and not D::C::A::a.
-  //constexpr D d{12};
-  //static_assert(d.B::a == 12);
-  //static_assert(d.C::a == 0);
+  constexpr D d{12};
+  static_assert(d.B::a == 12);
+  static_assert(d.C::a == 0);
 };
 #endif
 


        


More information about the cfe-commits mailing list