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

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Mon Jan 22 06:32:14 PST 2024


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

>From dcdb669e8d15ddfed5725efff9d9951306368f7f 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] [clang][Interp] Add inline descriptor to global variables

---
 clang/lib/AST/Interp/ByteCodeExprGen.cpp  | 17 ++++++++--
 clang/lib/AST/Interp/ByteCodeExprGen.h    |  8 ++++-
 clang/lib/AST/Interp/Descriptor.cpp       | 25 +++++++-------
 clang/lib/AST/Interp/Descriptor.h         | 11 +++++--
 clang/lib/AST/Interp/Interp.cpp           | 17 ++++++++++
 clang/lib/AST/Interp/Interp.h             | 40 ++++++++++++++++-------
 clang/lib/AST/Interp/InterpFrame.cpp      | 11 ++-----
 clang/lib/AST/Interp/Opcodes.td           |  5 ++-
 clang/lib/AST/Interp/Pointer.cpp          |  4 ++-
 clang/lib/AST/Interp/Pointer.h            | 28 +++++++++++-----
 clang/lib/AST/Interp/Program.cpp          | 36 ++++++++++++--------
 clang/test/AST/Interp/cxx11.cpp           | 10 +++---
 clang/test/AST/Interp/cxx17.cpp           | 23 +++++++++++--
 clang/test/AST/Interp/cxx23.cpp           | 24 ++++++++++----
 clang/test/AST/Interp/literals.cpp        | 17 ++++++++++
 clang/unittests/AST/Interp/Descriptor.cpp |  6 ++--
 16 files changed, 200 insertions(+), 82 deletions(-)

diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 065182811326859..0e2de02d01f6dca 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -829,7 +829,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>
@@ -857,13 +857,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/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index df4cb736299cb62..6bfaf6f2f7df64a 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -180,6 +180,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
     if (!visitInitializer(Init))
       return false;
 
+    if (!this->emitInitPtr(Init))
+      return false;
+
     return this->emitPopPtr(Init);
   }
 
@@ -191,6 +194,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
     if (!visitInitializer(Init))
       return false;
 
+    if (!this->emitInitPtr(Init))
+      return false;
+
     return this->emitPopPtr(Init);
   }
 
@@ -202,7 +208,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/Descriptor.cpp b/clang/lib/AST/Interp/Descriptor.cpp
index b330e54baf335a7..5701cf0acf915dc 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, const Descriptor *Elem,
+Descriptor::Descriptor(const DeclTy &D, const 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 580c200f9095296..6cca9d5feedede3 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.
@@ -128,15 +132,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, const Descriptor *Elem, bool IsTemporary,
-             UnknownSize);
+  Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD,
+             bool IsTemporary, UnknownSize);
 
   /// Allocates a descriptor for a record.
   Descriptor(const DeclTy &D, const Record *R, MetadataSize MD, bool IsConst,
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 807b860f3565d3a..be9ed81af69ecde 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -356,6 +356,23 @@ 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;
+
+  assert(S.getLangOpts().CPlusPlus);
+  const auto *VD = cast<VarDecl>(Ptr.getDeclDesc()->asValueDecl());
+  if ((!VD->hasConstantInitialization() &&
+       VD->mightBeUsableInConstantExpressions(S.getCtx())) ||
+      (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 (!CheckLive(S, OpPC, Ptr, AK_Read))
     return false;
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 65c54ed9c89b61c..875fe3f9870e568 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -88,6 +88,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);
@@ -1003,13 +1005,18 @@ 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 (!CheckConstant(S, OpPC, B->getDescriptor()))
+  const Pointer &Ptr = S.P.getPtrGlobal(I);
+  if (!CheckConstant(S, OpPC, Ptr.getFieldDesc()))
     return false;
-  if (B->isExtern())
+  if (Ptr.isExtern())
     return false;
-  S.Stk.push<T>(B->deref<T>());
+
+  // If a global variable is uninitialized, that means the initializer 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;
 }
 
@@ -1029,7 +1036,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;
 }
 
@@ -1045,7 +1054,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;
 }
 
@@ -1267,6 +1279,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;
@@ -1323,7 +1341,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;
@@ -1335,7 +1353,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;
@@ -1347,7 +1365,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()));
@@ -1362,7 +1380,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/InterpFrame.cpp b/clang/lib/AST/Interp/InterpFrame.cpp
index d460d7ea3710a88..dd05dac1703fd65 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/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index 24747b6b98c163e..1564457bb9984d6 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.cpp b/clang/lib/AST/Interp/Pointer.cpp
index 5af1d6d52e93e84..316a7ed5fa1bb82 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 8ccaff41ded8daf..7ae4617f2813795 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;
   }
@@ -284,6 +288,8 @@ class Pointer {
   bool isRoot() const {
     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; }
@@ -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 1daefab4dcdac1a..b2b478af2e73116 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,8 @@ unsigned Program::createGlobalString(const StringLiteral *S) {
   auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true,
                                        /*isExtern=*/false);
   G->block()->invokeCtor();
+
+  new (G->block()->rawData()) InlineDescriptor(Desc);
   Globals.push_back(G);
 
   // Construct the string in storage.
@@ -78,16 +80,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 +195,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 +212,8 @@ std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
       Global(getCurrentDecl(), Desc, IsStatic, IsExtern);
   G->block()->invokeCtor();
 
+  // Initialize InlineDescriptor fields.
+  new (G->block()->rawData()) InlineDescriptor(Desc);
   Globals.push_back(G);
 
   return I;
@@ -339,7 +347,7 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
         // Arrays of composites. In this case, the array is a list of pointers,
         // followed by the actual elements.
         const Descriptor *ElemDesc = createDescriptor(
-            D, ElemTy.getTypePtr(), std::nullopt, IsConst, IsTemporary);
+            D, ElemTy.getTypePtr(), MDSize, IsConst, IsTemporary);
         if (!ElemDesc)
           return nullptr;
         unsigned ElemSize =
@@ -355,14 +363,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 {
         const 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/cxx11.cpp b/clang/test/AST/Interp/cxx11.cpp
index 81e293fec175020..0a1e0f3fd28e9d0 100644
--- a/clang/test/AST/Interp/cxx11.cpp
+++ b/clang/test/AST/Interp/cxx11.cpp
@@ -1,8 +1,6 @@
 // RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify=both,expected -std=c++11 %s
 // RUN: %clang_cc1 -verify=both,ref -std=c++11 %s
 
-// expected-no-diagnostics
-
 namespace IntOrEnum {
   const int k = 0;
   const int &p = k;
@@ -17,8 +15,10 @@ template struct C<cval>;
 
 /// FIXME: This example does not get properly diagnosed in the new interpreter.
 extern const int recurse1;
-const int recurse2 = recurse1; // ref-note {{here}}
+const int recurse2 = recurse1; // both-note {{here}}
 const int recurse1 = 1;
 int array1[recurse1];
-int array2[recurse2]; // ref-warning 2{{variable length array}} \
-                      // ref-note {{initializer of 'recurse2' is not a constant expression}}
+int array2[recurse2]; // both-warning {{variable length arrays in C++}} \
+                      // both-note {{initializer of 'recurse2' is not a constant expression}} \
+                      // expected-error {{variable length array declaration not allowed at file scope}} \
+                      // ref-warning {{variable length array folded to constant array as an extension}}
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 133ab10023df0e0..a50a9b7183699a2 100644
--- a/clang/test/AST/Interp/cxx23.cpp
+++ b/clang/test/AST/Interp/cxx23.cpp
@@ -3,25 +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
 
-
 /// 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 77b75151a125598..c88e7c121480732 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, "");
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