[clang] [clang][Interp] IndirectMember initializers (PR #69900)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Tue Jan 16 20:55:03 PST 2024


Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/69900 at github.com>


https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/69900

>From 58ff884a20816526ea1b3e4035a65ab435e2396d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sun, 22 Oct 2023 19:47:33 +0200
Subject: [PATCH 1/5] [clang][Interp] IndirectMember initializers

---
 clang/lib/AST/Interp/ByteCodeStmtGen.cpp | 67 ++++++++++++++++--------
 clang/lib/AST/Interp/Interp.h            |  7 ++-
 clang/lib/AST/Interp/Opcodes.td          |  6 ++-
 clang/test/AST/Interp/records.cpp        | 60 +++++++++++++++++++++
 4 files changed, 114 insertions(+), 26 deletions(-)

diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
index b1ab5fcf9cb64c..7dca7c902b1c79 100644
--- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -142,6 +142,27 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
   // Classify the return type.
   ReturnType = this->classify(F->getReturnType());
 
+  auto emitFieldInitializer = [&](const Record::Field *F, unsigned FieldOffset,
+                                  const Expr *InitExpr) -> bool {
+    if (std::optional<PrimType> T = this->classify(InitExpr)) {
+      if (!this->visit(InitExpr))
+        return false;
+
+      if (F->isBitField())
+        return this->emitInitThisBitField(*T, F, FieldOffset, InitExpr);
+      return this->emitInitThisField(*T, FieldOffset, InitExpr);
+    }
+    // Non-primitive case. Get a pointer to the field-to-initialize
+    // on the stack and call visitInitialzer() for it.
+    if (!this->emitGetPtrThisField(FieldOffset, InitExpr))
+      return false;
+
+    if (!this->visitInitializer(InitExpr))
+      return false;
+
+    return this->emitPopPtr(InitExpr);
+  };
+
   // Emit custom code if this is a lambda static invoker.
   if (const auto *MD = dyn_cast<CXXMethodDecl>(F);
       MD && MD->isLambdaStaticInvoker())
@@ -162,29 +183,8 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
       if (const FieldDecl *Member = Init->getMember()) {
         const Record::Field *F = R->getField(Member);
 
-        if (std::optional<PrimType> T = this->classify(InitExpr)) {
-          if (!this->visit(InitExpr))
-            return false;
-
-          if (F->isBitField()) {
-            if (!this->emitInitThisBitField(*T, F, InitExpr))
-              return false;
-          } else {
-            if (!this->emitInitThisField(*T, F->Offset, InitExpr))
-              return false;
-          }
-        } else {
-          // Non-primitive case. Get a pointer to the field-to-initialize
-          // on the stack and call visitInitialzer() for it.
-          if (!this->emitGetPtrThisField(F->Offset, InitExpr))
-            return false;
-
-          if (!this->visitInitializer(InitExpr))
-            return false;
-
-          if (!this->emitPopPtr(InitExpr))
-            return false;
-        }
+        if (!emitFieldInitializer(F, F->Offset, InitExpr))
+          return false;
       } else if (const Type *Base = Init->getBaseClass()) {
         // Base class initializer.
         // Get This Base and call initializer on it.
@@ -198,6 +198,27 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
           return false;
         if (!this->emitInitPtrPop(InitExpr))
           return false;
+      } else if (const IndirectFieldDecl *IFD = Init->getIndirectMember()) {
+        assert(IFD->getChainingSize() >= 2);
+
+        unsigned NestedFieldOffset = 0;
+        const Record::Field *NestedField = nullptr;
+        for (const NamedDecl *ND : IFD->chain()) {
+          // FIXME: Can this *not* be a FieldDecl?
+          const FieldDecl *FD = cast<FieldDecl>(ND);
+          const Record *FieldRecord =
+              this->P.getOrCreateRecord(FD->getParent());
+          assert(FieldRecord);
+
+          NestedField = FieldRecord->getField(FD);
+          assert(NestedField);
+
+          NestedFieldOffset += NestedField->Offset;
+        }
+        assert(NestedField);
+
+        if (!emitFieldInitializer(NestedField, NestedFieldOffset, InitExpr))
+          return false;
       } else {
         assert(Init->isDelegatingInitializer());
         if (!this->emitThis(InitExpr))
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index dbbc4c09ce42a1..5321f9617feff2 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1078,15 +1078,18 @@ bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
   return true;
 }
 
+// FIXME: The Field pointer here is too much IMO and we could instead just
+// pass an Offset + BitWidth pair.
 template <PrimType Name, class T = typename PrimConv<Name>::T>
-bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
+bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F,
+                      uint32_t FieldOffset) {
   assert(F->isBitField());
   if (S.checkingPotentialConstantExpression())
     return false;
   const Pointer &This = S.Current->getThis();
   if (!CheckThis(S, OpPC, This))
     return false;
-  const Pointer &Field = This.atField(F->Offset);
+  const Pointer &Field = This.atField(FieldOffset);
   const auto &Value = S.Stk.pop<T>();
   Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx()));
   Field.initialize();
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index e01b6b9eea7dbb..6e0f1c939460d0 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -417,7 +417,11 @@ def InitThisField : AccessOpcode;
 // [Value] -> []
 def InitThisFieldActive : AccessOpcode;
 // [Value] -> []
-def InitThisBitField : BitFieldOpcode;
+def InitThisBitField : Opcode {
+  let Types = [AluTypeClass];
+  let Args = [ArgRecordField, ArgUint32];
+  let HasGroup = 1;
+}
 // [Pointer, Value] -> []
 def InitField : AccessOpcode;
 // [Pointer, Value] -> []
diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index a1ced049dcedb8..dc1739c1b4ec5a 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -1133,3 +1133,63 @@ namespace AccessOnNullptr {
                                // ref-error {{not an integral constant expression}} \
                                // ref-note {{in call to 'a2()'}}
 }
+
+namespace IndirectFieldInit {
+#if __cplusplus >= 202002L
+  /// Primitive.
+  struct Nested1 {
+    struct {
+      int first;
+    };
+    int x;
+    constexpr Nested1(int x) : first(12), x() { x = 4; }
+    constexpr Nested1() : Nested1(42) {}
+  };
+  constexpr Nested1 N1{};
+  static_assert(N1.first == 12, "");
+
+  /// Composite.
+  struct Nested2 {
+    struct First { int x = 42; };
+    struct {
+      First first;
+    };
+    int x;
+    constexpr Nested2(int x) : first(12), x() { x = 4; }
+    constexpr Nested2() : Nested2(42) {}
+  };
+  constexpr Nested2 N2{};
+  static_assert(N2.first.x == 12, "");
+
+  /// Bitfield.
+  struct Nested3 {
+    struct {
+      unsigned first : 2;
+    };
+    int x;
+    constexpr Nested3(int x) : first(3), x() { x = 4; }
+    constexpr Nested3() : Nested3(42) {}
+  };
+
+  constexpr Nested3 N3{};
+  static_assert(N3.first == 3, "");
+
+  /// Test that we get the offset right if the
+  /// record has a base.
+  struct Nested4Base {
+    int a;
+    int b;
+    char c;
+  };
+  struct Nested4 : Nested4Base{
+    struct {
+      int first;
+    };
+    int x;
+    constexpr Nested4(int x) : first(123), x() { a = 1; b = 2; c = 3; x = 4; }
+    constexpr Nested4() : Nested4(42) {}
+  };
+  constexpr Nested4 N4{};
+  static_assert(N4.first == 123, "");
+#endif
+}

>From 92e585735350e1d0512ad3d26d5b09af30aae4cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 16 Jan 2024 17:34:16 +0100
Subject: [PATCH 2/5] add another test case

---
 clang/test/AST/Interp/records.cpp | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index dc1739c1b4ec5a..fcf340af42c19d 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -1191,5 +1191,17 @@ namespace IndirectFieldInit {
   };
   constexpr Nested4 N4{};
   static_assert(N4.first == 123, "");
+
+  struct S {
+    struct {
+      int x, y;
+    };
+
+    constexpr S(int x_, int y_) : x(x_), y(y_) {}
+  };
+
+  constexpr S s(1, 2);
+  static_assert(s.x == 1 && s.y == 2);
+
 #endif
 }

>From c26fae9f4a8c1f97083947c3c0ebe5c8d8049444 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 16 Jan 2024 17:35:01 +0100
Subject: [PATCH 3/5] Remove fixme comment

---
 clang/lib/AST/Interp/ByteCodeStmtGen.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
index 7dca7c902b1c79..96308f7b5f0c1b 100644
--- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -204,7 +204,6 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
         unsigned NestedFieldOffset = 0;
         const Record::Field *NestedField = nullptr;
         for (const NamedDecl *ND : IFD->chain()) {
-          // FIXME: Can this *not* be a FieldDecl?
           const FieldDecl *FD = cast<FieldDecl>(ND);
           const Record *FieldRecord =
               this->P.getOrCreateRecord(FD->getParent());

>From 3051c09eb2f8518ba3e99a8cb0a76c4eef2c2c29 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 16 Jan 2024 17:50:29 +0100
Subject: [PATCH 4/5] Address review comments

---
 clang/lib/AST/Interp/ByteCodeStmtGen.cpp |  2 +-
 clang/test/AST/Interp/records.cpp        | 13 +++++++++++++
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
index 96308f7b5f0c1b..38067be73e2543 100644
--- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -204,7 +204,7 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
         unsigned NestedFieldOffset = 0;
         const Record::Field *NestedField = nullptr;
         for (const NamedDecl *ND : IFD->chain()) {
-          const FieldDecl *FD = cast<FieldDecl>(ND);
+          const auto *FD = cast<FieldDecl>(ND);
           const Record *FieldRecord =
               this->P.getOrCreateRecord(FD->getParent());
           assert(FieldRecord);
diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index fcf340af42c19d..5f90b3ea39995e 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -1203,5 +1203,18 @@ namespace IndirectFieldInit {
   constexpr S s(1, 2);
   static_assert(s.x == 1 && s.y == 2);
 
+  struct S2 {
+    struct {
+      struct {
+        int x, y;
+      };
+    };
+
+    constexpr S2(int x_, int y_) : x(x_), y(y_) {}
+  };
+
+  constexpr S2 s2(1, 2);
+  static_assert(s2.x == 1 && s2.y == 2);
+
 #endif
 }

>From 25dda3a8ad9f5e99c6663999b08f4e63645819b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Wed, 17 Jan 2024 05:54:41 +0100
Subject: [PATCH 5/5] Expand on last test case

---
 clang/test/AST/Interp/records.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index 5f90b3ea39995e..800dc6f910be6b 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -1204,17 +1204,19 @@ namespace IndirectFieldInit {
   static_assert(s.x == 1 && s.y == 2);
 
   struct S2 {
+    int a;
     struct {
+      int b;
       struct {
         int x, y;
       };
     };
 
-    constexpr S2(int x_, int y_) : x(x_), y(y_) {}
+    constexpr S2(int x_, int y_) : a(3), b(4), x(x_), y(y_) {}
   };
 
   constexpr S2 s2(1, 2);
-  static_assert(s2.x == 1 && s2.y == 2);
+  static_assert(s2.x == 1 && s2.y == 2 && s2.a == 3 && s2.b == 4);
 
 #endif
 }



More information about the cfe-commits mailing list