[clang] [clang][bytecode] Explicitly composite array descriptor types (PR #129376)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Sat Mar 1 02:49:18 PST 2025


https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/129376

When creating descriptor for array element types, we only save the original source, e.g. int[2][2][2]. So later calls to getType() of the element descriptors will also return int[2][2][2], instead of e.g. int[2][2] for the second dimension.
Fix this by explicitly tracking the array types.
The last attached test case used to have an lvalue offset of 32 instead of 24.

We should do this for more desriptor types though and not just composite array, but I'm leaving that to a later patch.

>From 82c307fd468a481096d64f7270e1eea4a7f503ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sat, 1 Mar 2025 11:38:08 +0100
Subject: [PATCH] [clang][bytecode] Explicitly composite array descriptor types

When creating descriptor for array element types, we only save the
original source, e.g. int[2][2][2]. So later calls to getType() of the
element descriptors will also return int[2][2][2], instead of e.g.
int[2][2] for the second dimension.
Fix this by explicitly tracking the array types.
The last attached test case used to have an lvalue offset of 32 instead
of 24.

We should do this for more desriptor types though and not just composite
array, but I'm leaving that to a later patch.
---
 clang/lib/AST/ByteCode/Descriptor.cpp       |  8 ++-
 clang/lib/AST/ByteCode/Descriptor.h         |  6 +-
 clang/lib/AST/ByteCode/DynamicAllocator.cpp |  4 +-
 clang/lib/AST/ByteCode/Program.cpp          |  2 +-
 clang/unittests/AST/ByteCode/toAPValue.cpp  | 67 +++++++++++++++++++++
 5 files changed, 81 insertions(+), 6 deletions(-)

diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp
index 6017f6dd61cb3..bcd9f6f3d078a 100644
--- a/clang/lib/AST/ByteCode/Descriptor.cpp
+++ b/clang/lib/AST/ByteCode/Descriptor.cpp
@@ -367,10 +367,12 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
 }
 
 /// Arrays of composite elements.
-Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
+Descriptor::Descriptor(const DeclTy &D, const Type *SourceTy,
+                       const Descriptor *Elem, MetadataSize MD,
                        unsigned NumElems, bool IsConst, bool IsTemporary,
                        bool IsMutable)
-    : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
+    : Source(D), SourceType(SourceTy),
+      ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
       Size(ElemSize * NumElems), MDSize(MD.value_or(0)),
       AllocSize(std::max<size_t>(alignof(void *), Size) + MDSize),
       ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable),
@@ -410,6 +412,8 @@ Descriptor::Descriptor(const DeclTy &D)
 }
 
 QualType Descriptor::getType() const {
+  if (SourceType)
+    return QualType(SourceType, 0);
   if (const auto *D = asValueDecl())
     return D->getType();
   if (const auto *T = dyn_cast_if_present<TypeDecl>(asDecl()))
diff --git a/clang/lib/AST/ByteCode/Descriptor.h b/clang/lib/AST/ByteCode/Descriptor.h
index 01fa4b198de67..b2e9f1b6bded4 100644
--- a/clang/lib/AST/ByteCode/Descriptor.h
+++ b/clang/lib/AST/ByteCode/Descriptor.h
@@ -124,6 +124,7 @@ struct Descriptor final {
 private:
   /// Original declaration, used to emit the error message.
   const DeclTy Source;
+  const Type *SourceType = nullptr;
   /// Size of an element, in host bytes.
   const unsigned ElemSize;
   /// Size of the storage, in host bytes.
@@ -186,8 +187,9 @@ struct Descriptor final {
              bool IsTemporary, UnknownSize);
 
   /// Allocates a descriptor for an array of composites.
-  Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
-             unsigned NumElems, bool IsConst, bool IsTemporary, bool IsMutable);
+  Descriptor(const DeclTy &D, const Type *SourceTy, const Descriptor *Elem,
+             MetadataSize MD, unsigned NumElems, bool IsConst, bool IsTemporary,
+             bool IsMutable);
 
   /// Allocates a descriptor for an array of composites of unknown size.
   Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
diff --git a/clang/lib/AST/ByteCode/DynamicAllocator.cpp b/clang/lib/AST/ByteCode/DynamicAllocator.cpp
index 3ef8c2e1f3e7c..728bd75d7d141 100644
--- a/clang/lib/AST/ByteCode/DynamicAllocator.cpp
+++ b/clang/lib/AST/ByteCode/DynamicAllocator.cpp
@@ -57,8 +57,10 @@ Block *DynamicAllocator::allocate(const Descriptor *ElementDesc,
   assert(ElementDesc->getMetadataSize() == 0);
   // Create a new descriptor for an array of the specified size and
   // element type.
+  // FIXME: Pass proper element type.
   const Descriptor *D = allocateDescriptor(
-      ElementDesc->asExpr(), ElementDesc, Descriptor::InlineDescMD, NumElements,
+      ElementDesc->asExpr(), nullptr, ElementDesc, Descriptor::InlineDescMD,
+      NumElements,
       /*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false);
   return allocate(D, EvalID, AllocForm);
 }
diff --git a/clang/lib/AST/ByteCode/Program.cpp b/clang/lib/AST/ByteCode/Program.cpp
index 0754e259b7cb3..cc2cfc9b03b41 100644
--- a/clang/lib/AST/ByteCode/Program.cpp
+++ b/clang/lib/AST/ByteCode/Program.cpp
@@ -419,7 +419,7 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
         unsigned ElemSize = ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
         if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems)
           return {};
-        return allocateDescriptor(D, ElemDesc, MDSize, NumElems, IsConst,
+        return allocateDescriptor(D, Ty, ElemDesc, MDSize, NumElems, IsConst,
                                   IsTemporary, IsMutable);
       }
     }
diff --git a/clang/unittests/AST/ByteCode/toAPValue.cpp b/clang/unittests/AST/ByteCode/toAPValue.cpp
index cd62338ee23c1..c7df97c81b16f 100644
--- a/clang/unittests/AST/ByteCode/toAPValue.cpp
+++ b/clang/unittests/AST/ByteCode/toAPValue.cpp
@@ -254,3 +254,70 @@ TEST(ToAPValue, MemberPointers) {
     ASSERT_EQ(A.getKind(), APValue::MemberPointer);
   }
 }
+
+/// Test the various toAPValue implementations.
+TEST(ToAPValue, Comparison) {
+  constexpr char Code[] =
+      "constexpr int GI = 12;\n"
+      "constexpr const int *PI = &GI;\n"
+      "struct S{int a; };\n"
+      "constexpr S GS[] = {{}, {}};\n"
+      "constexpr const S* OS = GS + 1;\n"
+      "constexpr S DS = *OS;\n"
+      "constexpr int IA[2][2][2] = {{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}};\n"
+      "constexpr const int *PIA = IA[1][1];\n";
+
+  for (const char *Arg : {"", "-fexperimental-new-constant-interpreter"}) {
+    auto AST = tooling::buildASTFromCodeWithArgs(Code, {Arg});
+
+    // auto &ASTCtx = AST->getASTContext();
+    auto getDecl = [&](const char *Name) -> const VarDecl * {
+      auto Nodes =
+          match(valueDecl(hasName(Name)).bind("var"), AST->getASTContext());
+      assert(Nodes.size() == 1);
+      const auto *D = Nodes[0].getNodeAs<ValueDecl>("var");
+      assert(D);
+      return cast<VarDecl>(D);
+    };
+
+    {
+      const VarDecl *GIDecl = getDecl("GI");
+      const APValue *V = GIDecl->evaluateValue();
+      ASSERT_TRUE(V->isInt());
+    }
+
+    {
+      const VarDecl *GIDecl = getDecl("GI");
+      const VarDecl *PIDecl = getDecl("PI");
+      const APValue *V = PIDecl->evaluateValue();
+      ASSERT_TRUE(V->isLValue());
+      ASSERT_TRUE(V->hasLValuePath());
+      ASSERT_EQ(V->getLValueBase().get<const ValueDecl *>(), GIDecl);
+      ASSERT_EQ(V->getLValuePath().size(), 0u);
+    }
+
+    {
+      const APValue *V = getDecl("OS")->evaluateValue();
+      ASSERT_TRUE(V->isLValue());
+      ASSERT_EQ(V->getLValueOffset().getQuantity(), (unsigned)sizeof(int));
+      ASSERT_TRUE(V->hasLValuePath());
+      ASSERT_EQ(V->getLValuePath().size(), 1u);
+      ASSERT_EQ(V->getLValuePath()[0].getAsArrayIndex(), 1u);
+    }
+
+    {
+      const APValue *V = getDecl("DS")->evaluateValue();
+      ASSERT_TRUE(V->isStruct());
+    }
+    {
+      const APValue *V = getDecl("PIA")->evaluateValue();
+      ASSERT_TRUE(V->isLValue());
+      ASSERT_TRUE(V->hasLValuePath());
+      ASSERT_EQ(V->getLValuePath().size(), 3u);
+      ASSERT_EQ(V->getLValuePath()[0].getAsArrayIndex(), 1u);
+      ASSERT_EQ(V->getLValuePath()[1].getAsArrayIndex(), 1u);
+      ASSERT_EQ(V->getLValuePath()[2].getAsArrayIndex(), 0u);
+      ASSERT_EQ(V->getLValueOffset().getQuantity(), (unsigned)sizeof(int) * 6);
+    }
+  }
+}



More information about the cfe-commits mailing list