[clang] [clang][Interp] Add inline descriptor to global variables (PR #72892)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 23 02:19:13 PST 2023


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/72892 at github.com>


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

>From b5360dd44bd5d8f10287e0a3641b0846e051a5c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 20 Nov 2023 11:53:40 +0100
Subject: [PATCH 1/3] [clang][Interp] Add inline descriptor to global variables

---
 clang/lib/AST/Interp/ByteCodeExprGen.cpp | 15 ++++++-
 clang/lib/AST/Interp/Descriptor.cpp      | 27 ++++++------
 clang/lib/AST/Interp/Descriptor.h        |  6 ++-
 clang/lib/AST/Interp/Interp.cpp          | 16 +++++++
 clang/lib/AST/Interp/Interp.h            | 23 +++++++---
 clang/lib/AST/Interp/Pointer.cpp         |  4 +-
 clang/lib/AST/Interp/Pointer.h           | 30 +++++++++----
 clang/lib/AST/Interp/Program.cpp         | 56 +++++++++++++++++-------
 clang/test/AST/Interp/cxx17.cpp          | 23 ++++++++--
 clang/test/AST/Interp/cxx23.cpp          | 27 +++++++-----
 clang/test/AST/Interp/literals.cpp       | 17 +++++++
 11 files changed, 185 insertions(+), 59 deletions(-)

diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 5dc1f9dfb10ff32..180749a75ebd22e 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -662,13 +662,26 @@ bool ByteCodeExprGen<Emitter>::VisitInitListExpr(const InitListExpr *E) {
     return this->visitInitList(E->inits(), E);
 
   if (T->isArrayType()) {
-    // FIXME: Array fillers.
     unsigned ElementIndex = 0;
     for (const Expr *Init : E->inits()) {
       if (!this->visitArrayElemInit(ElementIndex, Init))
         return false;
       ++ElementIndex;
     }
+
+    // Expand the filler expression.
+    // FIXME: This should go away.
+    if (const Expr *Filler = E->getArrayFiller()) {
+      const ConstantArrayType *CAT =
+          Ctx.getASTContext().getAsConstantArrayType(E->getType());
+      uint64_t NumElems = CAT->getSize().getZExtValue();
+
+      for (; ElementIndex != NumElems; ++ElementIndex) {
+        if (!this->visitArrayElemInit(ElementIndex, Filler))
+          return false;
+      }
+    }
+
     return true;
   }
 
diff --git a/clang/lib/AST/Interp/Descriptor.cpp b/clang/lib/AST/Interp/Descriptor.cpp
index 59a952135a2d809..7330295132618ed 100644
--- a/clang/lib/AST/Interp/Descriptor.cpp
+++ b/clang/lib/AST/Interp/Descriptor.cpp
@@ -243,18 +243,19 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
                        bool IsMutable)
     : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
       MDSize(MD.value_or(0)),
-      AllocSize(align(Size) + sizeof(InitMapPtr) + MDSize), IsConst(IsConst),
-      IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true),
-      CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
-      MoveFn(getMoveArrayPrim(Type)) {
+      AllocSize(align(MDSize) + align(Size) + sizeof(InitMapPtr)),
+      IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
+      IsArray(true), CtorFn(getCtorArrayPrim(Type)),
+      DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) {
   assert(Source && "Missing source");
 }
 
 /// Primitive unknown-size arrays.
-Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary,
-                       UnknownSize)
-    : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), MDSize(0),
-      AllocSize(alignof(void *) + sizeof(InitMapPtr)), IsConst(true),
+Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
+                       bool IsTemporary, UnknownSize)
+    : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark),
+      MDSize(MD.value_or(0)),
+      AllocSize(MDSize + sizeof(InitMapPtr) + alignof(void *)), IsConst(true),
       IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
       CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
       MoveFn(getMoveArrayPrim(Type)) {
@@ -275,12 +276,12 @@ Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
 }
 
 /// Unknown-size arrays of composite elements.
-Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary,
-                       UnknownSize)
+Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD,
+                       bool IsTemporary, UnknownSize)
     : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
-      Size(UnknownSizeMark), MDSize(0),
-      AllocSize(alignof(void *) + sizeof(InitMapPtr)), ElemDesc(Elem),
-      IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
+      Size(UnknownSizeMark), MDSize(MD.value_or(0)),
+      AllocSize(MDSize + alignof(void *)), ElemDesc(Elem), IsConst(true),
+      IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
       CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
   assert(Source && "Missing source");
 }
diff --git a/clang/lib/AST/Interp/Descriptor.h b/clang/lib/AST/Interp/Descriptor.h
index 8135f3d12f7035c..b0ffc21b7ea960f 100644
--- a/clang/lib/AST/Interp/Descriptor.h
+++ b/clang/lib/AST/Interp/Descriptor.h
@@ -128,14 +128,16 @@ struct Descriptor final {
              bool IsConst, bool IsTemporary, bool IsMutable);
 
   /// Allocates a descriptor for an array of primitives of unknown size.
-  Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize);
+  Descriptor(const DeclTy &D, PrimType Type, MetadataSize MDSize,
+             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);
 
   /// Allocates a descriptor for an array of composites of unknown size.
-  Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, UnknownSize);
+  Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD,
+             bool IsTemporary, UnknownSize);
 
   /// Allocates a descriptor for a record.
   Descriptor(const DeclTy &D, Record *R, MetadataSize MD, bool IsConst,
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 13b77e9a87725c7..69d6504696311df 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -289,6 +289,22 @@ bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
   return false;
 }
 
+bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+  if (Ptr.isInitialized())
+    return true;
+
+  const VarDecl *VD = cast<VarDecl>(Ptr.getDeclDesc()->asValueDecl());
+  if ((S.getLangOpts().CPlusPlus && !VD->hasConstantInitialization() &&
+       VD->mightBeUsableInConstantExpressions(S.getCtx())) ||
+      ((S.getLangOpts().CPlusPlus || S.getLangOpts().OpenCL) &&
+       !S.getLangOpts().CPlusPlus11 && !VD->hasICEInitializer(S.getCtx()))) {
+    const SourceInfo &Loc = S.Current->getSource(OpPC);
+    S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD;
+    S.Note(VD->getLocation(), diag::note_declared_at);
+  }
+  return false;
+}
+
 bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
   if (!CheckDummy(S, OpPC, Ptr))
     return false;
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 4f7778bdd2ff333..3a665c12d663b25 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -86,6 +86,8 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
 
 bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
                       AccessKinds AK);
+/// Check if a global variable is initialized.
+bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
 
 /// Checks if a value can be stored in a block.
 bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
@@ -1004,10 +1006,16 @@ bool SetThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
 
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
-  const Block *B = S.P.getGlobal(I);
-  if (B->isExtern())
+  const Pointer &Ptr = S.P.getPtrGlobal(I);
+  if (Ptr.isExtern())
     return false;
-  S.Stk.push<T>(B->deref<T>());
+
+  // If a global variable is uninitialized, that means the initialize we've
+  // compiled for it wasn't a constant expression. Diagnose that.
+  if (!CheckGlobalInitialized(S, OpPC, Ptr))
+    return false;
+
+  S.Stk.push<T>(Ptr.deref<T>());
   return true;
 }
 
@@ -1019,7 +1027,9 @@ bool SetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
 
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
-  S.P.getGlobal(I)->deref<T>() = S.Stk.pop<T>();
+  const Pointer &P = S.P.getGlobal(I);
+  P.deref<T>() = S.Stk.pop<T>();
+  P.initialize();
   return true;
 }
 
@@ -1035,7 +1045,10 @@ bool InitGlobalTemp(InterpState &S, CodePtr OpPC, uint32_t I,
   APValue *Cached = Temp->getOrCreateValue(true);
   *Cached = APV;
 
-  S.P.getGlobal(I)->deref<T>() = S.Stk.pop<T>();
+  const Pointer &P = S.P.getGlobal(I);
+  P.deref<T>() = S.Stk.pop<T>();
+  P.initialize();
+
   return true;
 }
 
diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp
index e979b99b0fdd0a0..88b945b7902cf22 100644
--- a/clang/lib/AST/Interp/Pointer.cpp
+++ b/clang/lib/AST/Interp/Pointer.cpp
@@ -19,7 +19,9 @@
 using namespace clang;
 using namespace clang::interp;
 
-Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {}
+Pointer::Pointer(Block *Pointee)
+    : Pointer(Pointee, Pointee->getDescriptor()->getMetadataSize(),
+              Pointee->getDescriptor()->getMetadataSize()) {}
 
 Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset)
     : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h
index a8f6e62fa76d356..f5e76b253931c8b 100644
--- a/clang/lib/AST/Interp/Pointer.h
+++ b/clang/lib/AST/Interp/Pointer.h
@@ -134,7 +134,8 @@ class Pointer {
 
     // Pointer to an array of base types - enter block.
     if (Base == RootPtrMark)
-      return Pointer(Pointee, 0, Offset == 0 ? Offset : PastEndMark);
+      return Pointer(Pointee, sizeof(InlineDescriptor),
+                     Offset == 0 ? Offset : PastEndMark);
 
     // Pointer is one past end - magic offset marks that.
     if (isOnePastEnd())
@@ -179,7 +180,7 @@ class Pointer {
       return *this;
 
     // If at base, point to an array of base types.
-    if (Base == 0)
+    if (Base == 0 || Base == sizeof(InlineDescriptor))
       return Pointer(Pointee, RootPtrMark, 0);
 
     // Step into the containing array, if inside one.
@@ -196,7 +197,10 @@ class Pointer {
   /// Checks if the pointer is live.
   bool isLive() const { return Pointee && !Pointee->IsDead; }
   /// Checks if the item is a field in an object.
-  bool isField() const { return Base != 0 && Base != RootPtrMark; }
+  bool isField() const {
+    return Base != 0 && Base != sizeof(InlineDescriptor) &&
+           Base != RootPtrMark && getFieldDesc()->asDecl();
+  }
 
   /// Accessor for information about the declaration site.
   const Descriptor *getDeclDesc() const {
@@ -227,7 +231,7 @@ class Pointer {
 
   /// Accessors for information about the innermost field.
   const Descriptor *getFieldDesc() const {
-    if (Base == 0 || Base == RootPtrMark)
+    if (Base == 0 || Base == sizeof(InlineDescriptor) || Base == RootPtrMark)
       return getDeclDesc();
     return getInlineDesc()->Desc;
   }
@@ -282,7 +286,9 @@ class Pointer {
   bool isArrayElement() const { return inArray() && Base != Offset; }
   /// Pointer points directly to a block.
   bool isRoot() const {
-    return (Base == 0 || Base == RootPtrMark) && Offset == 0;
+    return (Base == 0 || Base == sizeof(InlineDescriptor) ||
+            Base == RootPtrMark) &&
+           Offset == 0;
   }
 
   /// Returns the record descriptor of a class.
@@ -315,12 +321,16 @@ class Pointer {
 
   /// Checks if the field is mutable.
   bool isMutable() const {
-    return Base != 0 && getInlineDesc()->IsFieldMutable;
+    return Base != 0 && Base != sizeof(InlineDescriptor) &&
+           getInlineDesc()->IsFieldMutable;
   }
   /// Checks if an object was initialized.
   bool isInitialized() const;
   /// Checks if the object is active.
-  bool isActive() const { return Base == 0 || getInlineDesc()->IsActive; }
+  bool isActive() const {
+    return Base == 0 || Base == sizeof(InlineDescriptor) ||
+           getInlineDesc()->IsActive;
+  }
   /// Checks if a structure is a base class.
   bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
   /// Checks if the pointer pointers to a dummy value.
@@ -328,7 +338,9 @@ class Pointer {
 
   /// Checks if an object or a subfield is mutable.
   bool isConst() const {
-    return Base == 0 ? getDeclDesc()->IsConst : getInlineDesc()->IsConst;
+    return (Base == 0 || Base == sizeof(InlineDescriptor))
+               ? getDeclDesc()->IsConst
+               : getInlineDesc()->IsConst;
   }
 
   /// Returns the declaration ID.
@@ -353,7 +365,7 @@ class Pointer {
       return 1;
 
     // narrow()ed element in a composite array.
-    if (Base > 0 && Base == Offset)
+    if (Base > sizeof(InlineDescriptor) && Base == Offset)
       return 0;
 
     if (auto ElemSize = elemSize())
diff --git a/clang/lib/AST/Interp/Program.cpp b/clang/lib/AST/Interp/Program.cpp
index 52e13398163ecf7..fbcc358f9ae9348 100644
--- a/clang/lib/AST/Interp/Program.cpp
+++ b/clang/lib/AST/Interp/Program.cpp
@@ -54,11 +54,11 @@ unsigned Program::createGlobalString(const StringLiteral *S) {
   }
 
   // Create a descriptor for the string.
-  Descriptor *Desc =
-      allocateDescriptor(S, CharType, std::nullopt, S->getLength() + 1,
-                         /*isConst=*/true,
-                         /*isTemporary=*/false,
-                         /*isMutable=*/false);
+  Descriptor *Desc = allocateDescriptor(S, CharType, Descriptor::InlineDescMD,
+                                        S->getLength() + 1,
+                                        /*isConst=*/true,
+                                        /*isTemporary=*/false,
+                                        /*isMutable=*/false);
 
   // Allocate storage for the string.
   // The byte length does not include the null terminator.
@@ -67,6 +67,16 @@ unsigned Program::createGlobalString(const StringLiteral *S) {
   auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true,
                                        /*isExtern=*/false);
   G->block()->invokeCtor();
+
+  InlineDescriptor *ID =
+      reinterpret_cast<InlineDescriptor *>(G->block()->rawData());
+  ID->Offset = sizeof(InlineDescriptor);
+  ID->Desc = Desc;
+  ID->IsConst = true;
+  ID->IsInitialized = true;
+  ID->IsBase = false;
+  ID->IsActive = true;
+  ID->IsFieldMutable = false;
   Globals.push_back(G);
 
   // Construct the string in storage.
@@ -78,16 +88,19 @@ unsigned Program::createGlobalString(const StringLiteral *S) {
       case PT_Sint8: {
         using T = PrimConv<PT_Sint8>::T;
         Field.deref<T>() = T::from(CodePoint, BitWidth);
+        Field.initialize();
         break;
       }
       case PT_Uint16: {
         using T = PrimConv<PT_Uint16>::T;
         Field.deref<T>() = T::from(CodePoint, BitWidth);
+        Field.initialize();
         break;
       }
       case PT_Uint32: {
         using T = PrimConv<PT_Uint32>::T;
         Field.deref<T>() = T::from(CodePoint, BitWidth);
+        Field.initialize();
         break;
       }
       default:
@@ -190,12 +203,13 @@ std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
   Descriptor *Desc;
   const bool IsConst = Ty.isConstQualified();
   const bool IsTemporary = D.dyn_cast<const Expr *>();
-  if (auto T = Ctx.classify(Ty)) {
-    Desc = createDescriptor(D, *T, std::nullopt, IsConst, IsTemporary);
-  } else {
-    Desc = createDescriptor(D, Ty.getTypePtr(), std::nullopt, IsConst,
-                            IsTemporary);
-  }
+  if (std::optional<PrimType> T = Ctx.classify(Ty))
+    Desc =
+        createDescriptor(D, *T, Descriptor::InlineDescMD, IsConst, IsTemporary);
+  else
+    Desc = createDescriptor(D, Ty.getTypePtr(), Descriptor::InlineDescMD,
+                            IsConst, IsTemporary);
+
   if (!Desc)
     return std::nullopt;
 
@@ -206,6 +220,18 @@ std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
       Global(getCurrentDecl(), Desc, IsStatic, IsExtern);
   G->block()->invokeCtor();
 
+  // Initialize InlineDescriptor fields.
+  InlineDescriptor *ID =
+      reinterpret_cast<InlineDescriptor *>(G->block()->rawData());
+  ID->Offset = sizeof(InlineDescriptor);
+  ID->Desc = Desc;
+  ID->IsConst = true;
+  ID->IsInitialized = true;
+  ID->IsInitialized = false;
+  ID->IsBase = false;
+  ID->IsActive = true;
+  ID->IsFieldMutable = false;
+
   Globals.push_back(G);
 
   return I;
@@ -338,8 +364,8 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
       } else {
         // Arrays of composites. In this case, the array is a list of pointers,
         // followed by the actual elements.
-        Descriptor *ElemDesc = createDescriptor(
-            D, ElemTy.getTypePtr(), std::nullopt, IsConst, IsTemporary);
+        Descriptor *ElemDesc = createDescriptor(D, ElemTy.getTypePtr(), MDSize,
+                                                IsConst, IsTemporary);
         if (!ElemDesc)
           return nullptr;
         unsigned ElemSize =
@@ -355,14 +381,14 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
     // is forbidden on pointers to such objects.
     if (isa<IncompleteArrayType>(ArrayType)) {
       if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
-        return allocateDescriptor(D, *T, IsTemporary,
+        return allocateDescriptor(D, *T, MDSize, IsTemporary,
                                   Descriptor::UnknownSize{});
       } else {
         Descriptor *Desc = createDescriptor(D, ElemTy.getTypePtr(), MDSize,
                                             IsConst, IsTemporary);
         if (!Desc)
           return nullptr;
-        return allocateDescriptor(D, Desc, IsTemporary,
+        return allocateDescriptor(D, Desc, MDSize, IsTemporary,
                                   Descriptor::UnknownSize{});
       }
     }
diff --git a/clang/test/AST/Interp/cxx17.cpp b/clang/test/AST/Interp/cxx17.cpp
index e1f578a4418d9fe..76d985eb22e178a 100644
--- a/clang/test/AST/Interp/cxx17.cpp
+++ b/clang/test/AST/Interp/cxx17.cpp
@@ -1,9 +1,6 @@
 // RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++17 -verify %s
 // RUN: %clang_cc1 -std=c++17 -verify=ref %s
 
-// ref-no-diagnostics
-// expected-no-diagnostics
-
 struct F { int a; int b;};
 constexpr F getF() {
   return {12, 3};
@@ -83,3 +80,23 @@ constexpr int b() {
   return a[0] + a[1];
 }
 static_assert(b() == 11);
+
+/// The diagnostics between the two interpreters are different here.
+struct S { int a; };
+constexpr S getS() { // expected-error {{constexpr function never produces a constant expression}} \\
+                     // ref-error {{constexpr function never produces a constant expression}}
+  (void)(1/0); // expected-note 2{{division by zero}} \
+               // expected-warning {{division by zero}} \
+               // ref-note 2{{division by zero}} \
+               // ref-warning {{division by zero}}
+  return S{12};
+}
+constexpr S s = getS(); // expected-error {{must be initialized by a constant expression}} \
+                        // expected-note {{in call to 'getS()'}} \
+                        // ref-error {{must be initialized by a constant expression}} \\
+                        // ref-note {{in call to 'getS()'}} \
+                        // ref-note {{declared here}}
+static_assert(s.a == 12, ""); // expected-error {{not an integral constant expression}} \
+                              // expected-note {{read of uninitialized object}} \
+                              // ref-error {{not an integral constant expression}} \
+                              // ref-note {{initializer of 's' is not a constant expression}}
diff --git a/clang/test/AST/Interp/cxx23.cpp b/clang/test/AST/Interp/cxx23.cpp
index e284a66626fb331..8f9ffbe5d100b9d 100644
--- a/clang/test/AST/Interp/cxx23.cpp
+++ b/clang/test/AST/Interp/cxx23.cpp
@@ -3,28 +3,35 @@
 // RUN: %clang_cc1 -std=c++20 -fsyntax-only -fcxx-exceptions -verify=expected20 %s -fexperimental-new-constant-interpreter
 // RUN: %clang_cc1 -std=c++23 -fsyntax-only -fcxx-exceptions -verify=expected23 %s -fexperimental-new-constant-interpreter
 
-
-// expected23-no-diagnostics
-
-
 /// FIXME: The new interpreter is missing all the 'control flows through...' diagnostics.
 
 constexpr int f(int n) {  // ref20-error {{constexpr function never produces a constant expression}} \
-                          // ref23-error {{constexpr function never produces a constant expression}}
+                          // ref23-error {{constexpr function never produces a constant expression}} \
+                          // expected20-error {{constexpr function never produces a constant expression}} \
+                          // expected23-error {{constexpr function never produces a constant expression}}
   static const int m = n; // ref20-note {{control flows through the definition of a static variable}} \
                           // ref20-warning {{is a C++23 extension}} \
                           // ref23-note {{control flows through the definition of a static variable}} \
-                          // expected20-warning {{is a C++23 extension}}
+                          // expected20-warning {{is a C++23 extension}} \
+                          // expected20-note {{declared here}} \
+                          // expected23-note {{declared here}}
 
-  return m;
+  return m; // expected20-note {{initializer of 'm' is not a constant expression}} \
+            // expected23-note {{initializer of 'm' is not a constant expression}}
 }
 constexpr int g(int n) {        // ref20-error {{constexpr function never produces a constant expression}} \
-                                // ref23-error {{constexpr function never produces a constant expression}}
+                                // ref23-error {{constexpr function never produces a constant expression}} \
+                                // expected20-error {{constexpr function never produces a constant expression}} \
+                                // expected23-error {{constexpr function never produces a constant expression}}
   thread_local const int m = n; // ref20-note {{control flows through the definition of a thread_local variable}} \
                                 // ref20-warning {{is a C++23 extension}} \
                                 // ref23-note {{control flows through the definition of a thread_local variable}} \
-                                // expected20-warning {{is a C++23 extension}}
-  return m;
+                                // expected20-warning {{is a C++23 extension}} \
+                                // expected20-note {{declared here}} \
+                                // expected23-note {{declared here}}
+  return m; // expected20-note {{initializer of 'm' is not a constant expression}} \
+            // expected23-note {{initializer of 'm' is not a constant expression}}
+
 }
 
 constexpr int c_thread_local(int n) { // ref20-error {{constexpr function never produces a constant expression}} \
diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp
index 85adfe551384d27..6749dbc1abb314c 100644
--- a/clang/test/AST/Interp/literals.cpp
+++ b/clang/test/AST/Interp/literals.cpp
@@ -35,6 +35,23 @@ static_assert(one == 1, "");
 constexpr bool b2 = bool();
 static_assert(!b2, "");
 
+constexpr int Failed1 = 1 / 0; // expected-error {{must be initialized by a constant expression}} \
+                               // expected-note {{division by zero}} \
+                               // expected-note {{declared here}} \
+                               // ref-error {{must be initialized by a constant expression}} \
+                               // ref-note {{division by zero}} \
+                               // ref-note {{declared here}}
+constexpr int Failed2 = Failed1 + 1; // expected-error {{must be initialized by a constant expression}} \
+                                     // expected-note {{declared here}} \
+                                     // expected-note {{initializer of 'Failed1' is not a constant expression}} \
+                                     // ref-error {{must be initialized by a constant expression}} \
+                                     // ref-note {{declared here}} \
+                                     // ref-note {{initializer of 'Failed1' is not a constant expression}}
+static_assert(Failed2 == 0, ""); // expected-error {{not an integral constant expression}} \
+                                 // expected-note {{initializer of 'Failed2' is not a constant expression}} \
+                                 // ref-error {{not an integral constant expression}} \
+                                 // ref-note {{initializer of 'Failed2' is not a constant expression}}
+
 namespace ScalarTypes {
   constexpr int ScalarInitInt = int();
   static_assert(ScalarInitInt == 0, "");

>From 08c2bcfb31f34a7bb87a24de06da504d7b4a8386 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 21 Nov 2023 13:03:03 +0100
Subject: [PATCH 2/3] Use placement-new to default-initialize InlineDescriptors

---
 clang/lib/AST/Interp/Descriptor.h    |  4 ++++
 clang/lib/AST/Interp/InterpFrame.cpp | 11 ++---------
 clang/lib/AST/Interp/Program.cpp     | 22 ++--------------------
 3 files changed, 8 insertions(+), 29 deletions(-)

diff --git a/clang/lib/AST/Interp/Descriptor.h b/clang/lib/AST/Interp/Descriptor.h
index b0ffc21b7ea960f..957f1f7c10d840b 100644
--- a/clang/lib/AST/Interp/Descriptor.h
+++ b/clang/lib/AST/Interp/Descriptor.h
@@ -73,6 +73,10 @@ struct InlineDescriptor {
   unsigned IsFieldMutable : 1;
 
   const Descriptor *Desc;
+
+  InlineDescriptor(const Descriptor *D)
+      : Offset(sizeof(InlineDescriptor)), IsConst(false), IsInitialized(false),
+        IsBase(false), IsActive(false), IsFieldMutable(false), Desc(D) {}
 };
 
 /// Describes a memory block created by an allocation site.
diff --git a/clang/lib/AST/Interp/InterpFrame.cpp b/clang/lib/AST/Interp/InterpFrame.cpp
index b06923114c7a24e..e33f26cb9d16830 100644
--- a/clang/lib/AST/Interp/InterpFrame.cpp
+++ b/clang/lib/AST/Interp/InterpFrame.cpp
@@ -38,14 +38,7 @@ InterpFrame::InterpFrame(InterpState &S, const Function *Func,
     for (auto &Local : Scope.locals()) {
       Block *B = new (localBlock(Local.Offset)) Block(Local.Desc);
       B->invokeCtor();
-      InlineDescriptor *ID = localInlineDesc(Local.Offset);
-      ID->Desc = Local.Desc;
-      ID->IsActive = true;
-      ID->Offset = sizeof(InlineDescriptor);
-      ID->IsBase = false;
-      ID->IsFieldMutable = false;
-      ID->IsConst = false;
-      ID->IsInitialized = false;
+      new (localInlineDesc(Local.Offset)) InlineDescriptor(Local.Desc);
     }
   }
 }
@@ -201,7 +194,7 @@ const FunctionDecl *InterpFrame::getCallee() const {
 
 Pointer InterpFrame::getLocalPointer(unsigned Offset) const {
   assert(Offset < Func->getFrameSize() && "Invalid local offset.");
-  return Pointer(localBlock(Offset), sizeof(InlineDescriptor));
+  return Pointer(localBlock(Offset));
 }
 
 Pointer InterpFrame::getParamPointer(unsigned Off) {
diff --git a/clang/lib/AST/Interp/Program.cpp b/clang/lib/AST/Interp/Program.cpp
index fbcc358f9ae9348..eabc052469a28bc 100644
--- a/clang/lib/AST/Interp/Program.cpp
+++ b/clang/lib/AST/Interp/Program.cpp
@@ -68,15 +68,7 @@ unsigned Program::createGlobalString(const StringLiteral *S) {
                                        /*isExtern=*/false);
   G->block()->invokeCtor();
 
-  InlineDescriptor *ID =
-      reinterpret_cast<InlineDescriptor *>(G->block()->rawData());
-  ID->Offset = sizeof(InlineDescriptor);
-  ID->Desc = Desc;
-  ID->IsConst = true;
-  ID->IsInitialized = true;
-  ID->IsBase = false;
-  ID->IsActive = true;
-  ID->IsFieldMutable = false;
+  new (G->block()->rawData()) InlineDescriptor(Desc);
   Globals.push_back(G);
 
   // Construct the string in storage.
@@ -221,17 +213,7 @@ std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
   G->block()->invokeCtor();
 
   // Initialize InlineDescriptor fields.
-  InlineDescriptor *ID =
-      reinterpret_cast<InlineDescriptor *>(G->block()->rawData());
-  ID->Offset = sizeof(InlineDescriptor);
-  ID->Desc = Desc;
-  ID->IsConst = true;
-  ID->IsInitialized = true;
-  ID->IsInitialized = false;
-  ID->IsBase = false;
-  ID->IsActive = true;
-  ID->IsFieldMutable = false;
-
+  new (G->block()->rawData()) InlineDescriptor(Desc);
   Globals.push_back(G);
 
   return I;

>From 8a45752a717667cf87d60363e6e05dd37d1f7528 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 23 Nov 2023 11:18:35 +0100
Subject: [PATCH 3/3] Fix pointer initialization problems in tests

---
 clang/lib/AST/Interp/ByteCodeExprGen.cpp  |  2 +-
 clang/lib/AST/Interp/ByteCodeExprGen.h    |  8 +++++++-
 clang/lib/AST/Interp/Interp.h             | 14 ++++++++++----
 clang/lib/AST/Interp/Opcodes.td           |  5 ++---
 clang/lib/AST/Interp/Pointer.h            |  6 +++---
 clang/unittests/AST/Interp/Descriptor.cpp |  6 +++---
 6 files changed, 26 insertions(+), 15 deletions(-)

diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 180749a75ebd22e..2839ecf6d0dcf2d 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -634,7 +634,7 @@ bool ByteCodeExprGen<Emitter>::visitArrayElemInit(unsigned ElemIndex,
     return false;
   if (!this->visitInitializer(Init))
     return false;
-  return this->emitPopPtr(Init);
+  return this->emitInitPtrPop(Init);
 }
 
 template <class Emitter>
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index bc1d5d11a115135..df31d6ce2744699 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -179,6 +179,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
     if (!visitInitializer(Init))
       return false;
 
+    if (!this->emitInitPtr(Init))
+      return false;
+
     return this->emitPopPtr(Init);
   }
 
@@ -190,6 +193,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
     if (!visitInitializer(Init))
       return false;
 
+    if (!this->emitInitPtr(Init))
+      return false;
+
     if ((Init->getType()->isArrayType() || Init->getType()->isRecordType()) &&
         !this->emitCheckGlobalCtor(Init))
       return false;
@@ -205,7 +211,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
     if (!visitInitializer(I))
       return false;
 
-    return this->emitPopPtr(I);
+    return this->emitInitPtrPop(I);
   }
 
   bool visitInitList(ArrayRef<const Expr *> Inits, const Expr *E);
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 3a665c12d663b25..19b70213c0beb7f 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1263,6 +1263,12 @@ inline bool InitPtrPop(InterpState &S, CodePtr OpPC) {
   return true;
 }
 
+inline bool InitPtr(InterpState &S, CodePtr OpPC) {
+  const Pointer &Ptr = S.Stk.peek<Pointer>();
+  Ptr.initialize();
+  return true;
+}
+
 inline bool VirtBaseHelper(InterpState &S, CodePtr OpPC, const RecordDecl *Decl,
                            const Pointer &Ptr) {
   Pointer Base = Ptr;
@@ -1319,7 +1325,7 @@ bool Store(InterpState &S, CodePtr OpPC) {
   const Pointer &Ptr = S.Stk.peek<Pointer>();
   if (!CheckStore(S, OpPC, Ptr))
     return false;
-  if (!Ptr.isRoot())
+  if (Ptr.canBeInitialized())
     Ptr.initialize();
   Ptr.deref<T>() = Value;
   return true;
@@ -1331,7 +1337,7 @@ bool StorePop(InterpState &S, CodePtr OpPC) {
   const Pointer &Ptr = S.Stk.pop<Pointer>();
   if (!CheckStore(S, OpPC, Ptr))
     return false;
-  if (!Ptr.isRoot())
+  if (Ptr.canBeInitialized())
     Ptr.initialize();
   Ptr.deref<T>() = Value;
   return true;
@@ -1343,7 +1349,7 @@ bool StoreBitField(InterpState &S, CodePtr OpPC) {
   const Pointer &Ptr = S.Stk.peek<Pointer>();
   if (!CheckStore(S, OpPC, Ptr))
     return false;
-  if (!Ptr.isRoot())
+  if (Ptr.canBeInitialized())
     Ptr.initialize();
   if (const auto *FD = Ptr.getField())
     Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
@@ -1358,7 +1364,7 @@ bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) {
   const Pointer &Ptr = S.Stk.pop<Pointer>();
   if (!CheckStore(S, OpPC, Ptr))
     return false;
-  if (!Ptr.isRoot())
+  if (Ptr.canBeInitialized())
     Ptr.initialize();
   if (const auto *FD = Ptr.getField())
     Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index 69068e87d5720ab..8627efd3f21d330 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -317,9 +317,8 @@ def GetPtrBasePop : Opcode {
   let Args = [ArgUint32];
 }
 
-def InitPtrPop : Opcode {
-  let Args = [];
-}
+def InitPtrPop : Opcode;
+def InitPtr : Opcode;
 
 def GetPtrDerivedPop : Opcode {
   let Args = [ArgUint32];
diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h
index f5e76b253931c8b..3f8a9ac12020cce 100644
--- a/clang/lib/AST/Interp/Pointer.h
+++ b/clang/lib/AST/Interp/Pointer.h
@@ -286,10 +286,10 @@ class Pointer {
   bool isArrayElement() const { return inArray() && Base != Offset; }
   /// Pointer points directly to a block.
   bool isRoot() const {
-    return (Base == 0 || Base == sizeof(InlineDescriptor) ||
-            Base == RootPtrMark) &&
-           Offset == 0;
+    return (Base == 0 || Base == RootPtrMark) && Offset == 0;
   }
+  /// If this pointer has an InlineDescriptor we can use to initialize.
+  bool canBeInitialized() const { return Pointee && Base > 0; }
 
   /// Returns the record descriptor of a class.
   const Record *getRecord() const { return getFieldDesc()->ElemRecord; }
diff --git a/clang/unittests/AST/Interp/Descriptor.cpp b/clang/unittests/AST/Interp/Descriptor.cpp
index fb1690a97061890..4ea0fbc285a98e5 100644
--- a/clang/unittests/AST/Interp/Descriptor.cpp
+++ b/clang/unittests/AST/Interp/Descriptor.cpp
@@ -52,7 +52,7 @@ TEST(Descriptor, Primitives) {
   ASSERT_FALSE(GlobalDesc->asRecordDecl());
 
   // Still true because this is a global variable.
-  ASSERT_TRUE(GlobalDesc->getMetadataSize() == 0);
+  ASSERT_TRUE(GlobalDesc->getMetadataSize() == sizeof(InlineDescriptor));
   ASSERT_FALSE(GlobalDesc->isPrimitiveArray());
   ASSERT_FALSE(GlobalDesc->isCompositeArray());
   ASSERT_FALSE(GlobalDesc->isZeroSizeArray());
@@ -114,8 +114,8 @@ TEST(Descriptor, Primitives) {
   ASSERT_TRUE(F4->Desc->ElemDesc->isPrimitiveArray());
 
   // Check pointer stuff.
-  // Global variables have no inline descriptor (yet).
-  ASSERT_TRUE(GlobalPtr.isRoot());
+  // Global variables have an inline descriptor.
+  ASSERT_FALSE(GlobalPtr.isRoot());
   ASSERT_TRUE(GlobalPtr.isLive());
   ASSERT_FALSE(GlobalPtr.isZero());
   ASSERT_FALSE(GlobalPtr.isField());



More information about the cfe-commits mailing list