tbaeder updated this revision to Diff 477437.

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D135750/new/

https://reviews.llvm.org/D135750

Files:
  clang/lib/AST/Interp/ByteCodeExprGen.cpp
  clang/lib/AST/Interp/ByteCodeStmtGen.cpp
  clang/lib/AST/Interp/Context.cpp
  clang/lib/AST/Interp/Descriptor.cpp
  clang/lib/AST/Interp/Descriptor.h
  clang/lib/AST/Interp/EvalEmitter.cpp
  clang/lib/AST/Interp/Interp.h
  clang/lib/AST/Interp/InterpBlock.h
  clang/lib/AST/Interp/InterpFrame.cpp
  clang/lib/AST/Interp/InterpFrame.h
  clang/lib/AST/Interp/Pointer.cpp
  clang/lib/AST/Interp/Pointer.h
  clang/lib/AST/Interp/Program.cpp
  clang/lib/AST/Interp/Program.h
  clang/test/AST/Interp/cxx20.cpp
  clang/test/AST/Interp/literals.cpp
  clang/test/AST/Interp/loops.cpp

Index: clang/test/AST/Interp/loops.cpp
===================================================================
--- clang/test/AST/Interp/loops.cpp
+++ clang/test/AST/Interp/loops.cpp
@@ -5,6 +5,7 @@
 
 // ref-no-diagnostics
 // expected-no-diagnostics
+// expected-cpp20-no-diagnostics
 
 namespace WhileLoop {
   constexpr int f() {
@@ -165,8 +166,6 @@
   static_assert(f5(true) == 8, "");
   static_assert(f5(false) == 5, "");
 
-  /// FIXME: This should be accepted in C++20 but is currently being rejected
-  ///   because the variable declaration doesn't have an initializier.
 #if __cplusplus >= 202002L
   constexpr int f6() {
     int i;
@@ -176,7 +175,7 @@
     } while (true);
     return i;
   }
-  static_assert(f6() == 5, ""); // expected-cpp20-error {{not an integral constant}}
+  static_assert(f6() == 5, "");
 #endif
 
 #if 0
Index: clang/test/AST/Interp/literals.cpp
===================================================================
--- clang/test/AST/Interp/literals.cpp
+++ clang/test/AST/Interp/literals.cpp
@@ -407,8 +407,7 @@
     return 1;
   }
   static_assert(uninit(), ""); // ref-error {{not an integral constant expression}} \
-                               // ref-note {{in call to 'uninit()'}} \
-                               // expected-error {{not an integral constant expression}}
+                               // ref-note {{in call to 'uninit()'}}
 
   constexpr int OverFlow() { // ref-error {{never produces a constant expression}}
     int a = INT_MAX;
Index: clang/test/AST/Interp/cxx20.cpp
===================================================================
--- clang/test/AST/Interp/cxx20.cpp
+++ clang/test/AST/Interp/cxx20.cpp
@@ -56,33 +56,27 @@
 }
 static_assert(pointerAssign2() == 12, "");
 
-
 constexpr int unInitLocal() {
   int a;
-  return a; // ref-note{{read of uninitialized object}}
+  return a; // ref-note {{read of uninitialized object}} \
+            // expected-note {{read of object outside its lifetime}}
+            // FIXME: ^^^ Wrong diagnostic.
 }
-static_assert(unInitLocal() == 0, ""); // expected-error {{not an integral constant expression}} \
-                                       // ref-error {{not an integral constant expression}} \
-                                       // ref-note {{in call to 'unInitLocal()'}}
-
-/// TODO: The example above is correctly rejected by the new constexpr
-///   interpreter, but for the wrong reasons. We don't reject it because
-///   it is an uninitialized read, we reject it simply because
-///   the local variable does not have an initializer.
-///
-///   The code below should be accepted but is also being rejected
-///   right now.
-#if 0
+static_assert(unInitLocal() == 0, ""); // ref-error {{not an integral constant expression}} \
+                                       // ref-note {{in call to 'unInitLocal()'}} \
+                                       // expected-error {{not an integral constant expression}} \
+                                       // expected-note {{in call to 'unInitLocal()'}} \
+
 constexpr int initializedLocal() {
   int a;
-  int b;
-
   a = 20;
   return a;
 }
 static_assert(initializedLocal() == 20);
 
-/// Similar here, but the uninitialized local is passed as a function parameter.
+#if 0
+// FIXME: This code should be rejected because we pass an uninitialized value
+//   as a function parameter.
 constexpr int inc(int a) { return a + 1; }
 constexpr int f() {
     int i;
Index: clang/lib/AST/Interp/Program.h
===================================================================
--- clang/lib/AST/Interp/Program.h
+++ clang/lib/AST/Interp/Program.h
@@ -113,14 +113,15 @@
 
   /// Creates a descriptor for a primitive type.
   Descriptor *createDescriptor(const DeclTy &D, PrimType Type,
-                               bool IsConst = false,
-                               bool IsTemporary = false,
+                               Descriptor::MetadataSize MDSize = {0},
+                               bool IsConst = false, bool IsTemporary = false,
                                bool IsMutable = false) {
-    return allocateDescriptor(D, Type, IsConst, IsTemporary, IsMutable);
+    return allocateDescriptor(D, Type, MDSize, IsConst, IsTemporary, IsMutable);
   }
 
   /// Creates a descriptor for a composite type.
   Descriptor *createDescriptor(const DeclTy &D, const Type *Ty,
+                               Descriptor::MetadataSize MDSize = {0},
                                bool IsConst = false, bool IsTemporary = false,
                                bool IsMutable = false,
                                const Expr *Init = nullptr);
Index: clang/lib/AST/Interp/Program.cpp
===================================================================
--- clang/lib/AST/Interp/Program.cpp
+++ clang/lib/AST/Interp/Program.cpp
@@ -53,7 +53,7 @@
   }
 
   // Create a descriptor for the string.
-  Descriptor *Desc = allocateDescriptor(S, CharType, S->getLength() + 1,
+  Descriptor *Desc = allocateDescriptor(S, CharType, None, S->getLength() + 1,
                                         /*isConst=*/true,
                                         /*isTemporary=*/false,
                                         /*isMutable=*/false);
@@ -184,9 +184,9 @@
   const bool IsConst = Ty.isConstQualified();
   const bool IsTemporary = D.dyn_cast<const Expr *>();
   if (auto T = Ctx.classify(Ty)) {
-    Desc = createDescriptor(D, *T, IsConst, IsTemporary);
+    Desc = createDescriptor(D, *T, None, IsConst, IsTemporary);
   } else {
-    Desc = createDescriptor(D, Ty.getTypePtr(), IsConst, IsTemporary);
+    Desc = createDescriptor(D, Ty.getTypePtr(), None, IsConst, IsTemporary);
   }
   if (!Desc)
     return {};
@@ -235,7 +235,7 @@
   auto GetBaseDesc = [this](const RecordDecl *BD, Record *BR) -> Descriptor * {
     if (!BR)
       return nullptr;
-    return allocateDescriptor(BD, BR, /*isConst=*/false,
+    return allocateDescriptor(BD, BR, None, /*isConst=*/false,
                               /*isTemporary=*/false,
                               /*isMutable=*/false);
   };
@@ -285,10 +285,10 @@
     const bool IsMutable = FD->isMutable();
     Descriptor *Desc;
     if (llvm::Optional<PrimType> T = Ctx.classify(FT)) {
-      Desc = createDescriptor(FD, *T, IsConst, /*isTemporary=*/false,
-                              IsMutable);
+      Desc = createDescriptor(FD, *T, None, IsConst,
+                              /*isTemporary=*/false, IsMutable);
     } else {
-      Desc = createDescriptor(FD, FT.getTypePtr(), IsConst,
+      Desc = createDescriptor(FD, FT.getTypePtr(), None, IsConst,
                               /*isTemporary=*/false, IsMutable);
     }
     if (!Desc)
@@ -304,12 +304,14 @@
 }
 
 Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
+                                      Descriptor::MetadataSize MDSize,
                                       bool IsConst, bool IsTemporary,
                                       bool IsMutable, const Expr *Init) {
   // Classes and structures.
   if (auto *RT = Ty->getAs<RecordType>()) {
     if (auto *Record = getOrCreateRecord(RT->getDecl()))
-      return allocateDescriptor(D, Record, IsConst, IsTemporary, IsMutable);
+      return allocateDescriptor(D, Record, MDSize, IsConst, IsTemporary,
+                                IsMutable);
   }
 
   // Arrays.
@@ -324,21 +326,21 @@
         if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems) {
           return {};
         }
-        return allocateDescriptor(D, *T, NumElems, IsConst, IsTemporary,
+        return allocateDescriptor(D, *T, MDSize, NumElems, IsConst, IsTemporary,
                                   IsMutable);
       } 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(), IsConst, IsTemporary);
+        Descriptor *ElemDesc = createDescriptor(D, ElemTy.getTypePtr(), None,
+                                                IsConst, IsTemporary);
         if (!ElemDesc)
           return nullptr;
         InterpSize ElemSize =
             ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
         if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems)
           return {};
-        return allocateDescriptor(D, ElemDesc, NumElems, IsConst, IsTemporary,
-                                  IsMutable);
+        return allocateDescriptor(D, ElemDesc, MDSize, NumElems, IsConst,
+                                  IsTemporary, IsMutable);
       }
     }
 
@@ -349,8 +351,8 @@
         return allocateDescriptor(D, *T, IsTemporary,
                                   Descriptor::UnknownSize{});
       } else {
-        Descriptor *Desc =
-            createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary);
+        Descriptor *Desc = createDescriptor(D, ElemTy.getTypePtr(), MDSize,
+                                            IsConst, IsTemporary);
         if (!Desc)
           return nullptr;
         return allocateDescriptor(D, Desc, IsTemporary,
@@ -362,13 +364,15 @@
   // Atomic types.
   if (auto *AT = Ty->getAs<AtomicType>()) {
     const Type *InnerTy = AT->getValueType().getTypePtr();
-    return createDescriptor(D, InnerTy, IsConst, IsTemporary, IsMutable);
+    return createDescriptor(D, InnerTy, MDSize, IsConst, IsTemporary,
+                            IsMutable);
   }
 
   // Complex types - represented as arrays of elements.
   if (auto *CT = Ty->getAs<ComplexType>()) {
     PrimType ElemTy = *Ctx.classify(CT->getElementType());
-    return allocateDescriptor(D, ElemTy, 2, IsConst, IsTemporary, IsMutable);
+    return allocateDescriptor(D, ElemTy, MDSize, 2, IsConst, IsTemporary,
+                              IsMutable);
   }
 
   return nullptr;
Index: clang/lib/AST/Interp/Pointer.h
===================================================================
--- clang/lib/AST/Interp/Pointer.h
+++ clang/lib/AST/Interp/Pointer.h
@@ -33,6 +33,31 @@
 ///
 /// This object can be allocated into interpreter stack frames. If pointing to
 /// a live block, it is a link in the chain of pointers pointing to the block.
+///
+/// In the simplest form, a Pointer has a Block* (the pointee) and both Base
+/// and Offset are 0, which means it will point to raw data.
+///
+/// The Base field is used to access metadata about the data. For primitive
+/// arrays, the Base is followed by an InitMap. In a variety of cases, the
+/// Base is preceded by an InlineDescriptor, which is used to track the
+/// initialization state, among other things.
+///
+/// The Offset field is used to access the actual data. In other words, the
+/// data the pointer decribes can be found at
+/// Pointee->rawData() + Pointer.Offset.
+///
+///
+/// Pointee                      Offset
+/// │                              │
+/// │                              │
+/// ▼                              ▼
+/// ┌───────┬────────────┬─────────┬────────────────────────────┐
+/// │ Block │ InlineDesc │ InitMap │ Actual Data                │
+/// └───────┴────────────┴─────────┴────────────────────────────┘
+///                      ▲
+///                      │
+///                      │
+///                     Base
 class Pointer {
 private:
   static constexpr unsigned PastEndMark = (unsigned)-1;
@@ -41,6 +66,7 @@
 public:
   Pointer() {}
   Pointer(Block *B);
+  Pointer(Block *B, unsigned BaseAndOffset);
   Pointer(const Pointer &P);
   Pointer(Pointer &&P);
   ~Pointer();
@@ -276,12 +302,12 @@
   /// Dereferences the pointer, if it's live.
   template <typename T> T &deref() const {
     assert(isLive() && "Invalid pointer");
-    return *reinterpret_cast<T *>(Pointee->data() + Offset);
+    return *reinterpret_cast<T *>(Pointee->rawData() + Offset);
   }
 
   /// Dereferences a primitive element.
   template <typename T> T &elem(unsigned I) const {
-    return reinterpret_cast<T *>(Pointee->data())[I];
+    return reinterpret_cast<T *>(Pointee->rawData())[I];
   }
 
   /// Initializes a field.
@@ -318,12 +344,13 @@
   /// Returns a descriptor at a given offset.
   InlineDescriptor *getDescriptor(unsigned Offset) const {
     assert(Offset != 0 && "Not a nested pointer");
-    return reinterpret_cast<InlineDescriptor *>(Pointee->data() + Offset) - 1;
+    return reinterpret_cast<InlineDescriptor *>(Pointee->rawData() + Offset) -
+           1;
   }
 
   /// Returns a reference to the pointer which stores the initialization map.
   InitMap *&getInitMap() const {
-    return *reinterpret_cast<InitMap **>(Pointee->data() + Base);
+    return *reinterpret_cast<InitMap **>(Pointee->rawData() + Base);
   }
 
   /// The block the pointer is pointing to.
Index: clang/lib/AST/Interp/Pointer.cpp
===================================================================
--- clang/lib/AST/Interp/Pointer.cpp
+++ clang/lib/AST/Interp/Pointer.cpp
@@ -16,6 +16,9 @@
 
 Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {}
 
+Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset)
+    : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
+
 Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {}
 
 Pointer::Pointer(Pointer &&P)
Index: clang/lib/AST/Interp/InterpFrame.h
===================================================================
--- clang/lib/AST/Interp/InterpFrame.h
+++ clang/lib/AST/Interp/InterpFrame.h
@@ -33,7 +33,7 @@
 
   /// Creates a new frame for a method call.
   InterpFrame(InterpState &S, const Function *Func, InterpFrame *Caller,
-              CodePtr RetPC, Pointer &&This);
+              CodePtr RetPC);
 
   /// Creates a new frame with the values that make sense.
   /// I.e., the caller is the current frame of S,
@@ -75,10 +75,11 @@
   /// Mutates a local variable.
   template <typename T> void setLocal(unsigned Offset, const T &Value) {
     localRef<T>(Offset) = Value;
+    localInlineDesc(Offset)->IsInitialized = true;
   }
 
   /// Returns a pointer to a local variables.
-  Pointer getLocalPointer(unsigned Offset);
+  Pointer getLocalPointer(unsigned Offset) const;
 
   /// Returns the value of an argument.
   template <typename T> const T &getParam(unsigned Offset) const {
@@ -124,7 +125,7 @@
 
   /// Returns an offset to a local.
   template <typename T> T &localRef(unsigned Offset) const {
-    return *reinterpret_cast<T *>(Locals.get() + Offset);
+    return getLocalPointer(Offset).deref<T>();
   }
 
   /// Returns a pointer to a local's block.
@@ -132,6 +133,11 @@
     return Locals.get() + Offset - sizeof(Block);
   }
 
+  // Returns the inline descriptor of the local.
+  InlineDescriptor *localInlineDesc(unsigned Offset) const {
+    return reinterpret_cast<InlineDescriptor *>(Locals.get() + Offset);
+  }
+
 private:
   /// Reference to the interpreter state.
   InterpState &S;
Index: clang/lib/AST/Interp/InterpFrame.cpp
===================================================================
--- clang/lib/AST/Interp/InterpFrame.cpp
+++ clang/lib/AST/Interp/InterpFrame.cpp
@@ -20,8 +20,8 @@
 using namespace clang::interp;
 
 InterpFrame::InterpFrame(InterpState &S, const Function *Func,
-                         InterpFrame *Caller, CodePtr RetPC, Pointer &&This)
-    : Caller(Caller), S(S), Func(Func), This(std::move(This)), RetPC(RetPC),
+                         InterpFrame *Caller, CodePtr RetPC)
+    : Caller(Caller), S(S), Func(Func), RetPC(RetPC),
       ArgSize(Func ? Func->getArgSize() : 0),
       Args(static_cast<char *>(S.Stk.top())), FrameOffset(S.Stk.size()) {
   if (Func) {
@@ -31,6 +31,10 @@
         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 = Local.Offset;
         }
       }
     }
@@ -38,11 +42,7 @@
 }
 
 InterpFrame::InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC)
-    : Caller(S.Current), S(S), Func(Func), RetPC(RetPC),
-      ArgSize(Func ? Func->getArgSize() : 0),
-      Args(static_cast<char *>(S.Stk.top())), FrameOffset(S.Stk.size()) {
-  assert(Func);
-
+    : InterpFrame(S, Func, S.Current, RetPC) {
   // As per our calling convention, the this pointer is
   // part of the ArgSize.
   // If the function has RVO, the RVO pointer is first.
@@ -55,16 +55,6 @@
     else
       This = stackRef<Pointer>(0);
   }
-
-  if (unsigned FrameSize = Func->getFrameSize()) {
-    Locals = std::make_unique<char[]>(FrameSize);
-    for (auto &Scope : Func->scopes()) {
-      for (auto &Local : Scope.locals()) {
-        Block *B = new (localBlock(Local.Offset)) Block(Local.Desc);
-        B->invokeCtor();
-      }
-    }
-  }
 }
 
 InterpFrame::~InterpFrame() {
@@ -183,10 +173,10 @@
   return Func->getDecl();
 }
 
-Pointer InterpFrame::getLocalPointer(unsigned Offset) {
+Pointer InterpFrame::getLocalPointer(unsigned Offset) const {
   assert(Offset < Func->getFrameSize() && "Invalid local offset.");
-  return Pointer(
-      reinterpret_cast<Block *>(Locals.get() + Offset - sizeof(Block)));
+  return Pointer(reinterpret_cast<Block *>(localBlock(Offset)),
+                 sizeof(InlineDescriptor));
 }
 
 Pointer InterpFrame::getParamPointer(unsigned Off) {
Index: clang/lib/AST/Interp/InterpBlock.h
===================================================================
--- clang/lib/AST/Interp/InterpBlock.h
+++ clang/lib/AST/Interp/InterpBlock.h
@@ -31,7 +31,21 @@
 
 /// A memory block, either on the stack or in the heap.
 ///
-/// The storage described by the block immediately follows it in memory.
+/// The storage described by the block is immediately followed by
+/// optional metadata, which is followed by the actual data.
+///
+/// Block*        rawData()                  data()
+/// │               │                         │
+/// │               │                         │
+/// ▼               ▼                         ▼
+/// ┌───────────────┬─────────────────────────┬─────────────────┐
+/// │ Block         │ Metadata                │ Data            │
+/// │ sizeof(Block) │ Desc->getMetadataSize() │ Desc->getSize() │
+/// └───────────────┴─────────────────────────┴─────────────────┘
+///
+/// Desc->getAllocSize() describes the size after the Block, i.e.
+/// the data size and the metadata size.
+///
 class Block final {
 public:
   // Creates a new block.
@@ -59,7 +73,24 @@
   llvm::Optional<unsigned> getDeclID() const { return DeclID; }
 
   /// Returns a pointer to the stored data.
-  char *data() { return reinterpret_cast<char *>(this + 1); }
+  /// You are allowed to read Desc->getSize() bytes from this address.
+  char *data() {
+    // rawData might contain metadata as well.
+    size_t DataOffset = Desc->getMetadataSize();
+    return rawData() + DataOffset;
+  }
+  const char *data() const {
+    // rawData might contain metadata as well.
+    size_t DataOffset = Desc->getMetadataSize();
+    return rawData() + DataOffset;
+  }
+
+  /// Returns a pointer to the raw data, including metadata.
+  /// You are allowed to read Desc->getAllocSize() bytes from this address.
+  char *rawData() { return reinterpret_cast<char *>(this) + sizeof(Block); }
+  const char *rawData() const {
+    return reinterpret_cast<const char *>(this) + sizeof(Block);
+  }
 
   /// Returns a view over the data.
   template <typename T>
@@ -67,7 +98,7 @@
 
   /// Invokes the constructor.
   void invokeCtor() {
-    std::memset(data(), 0, getSize());
+    std::memset(rawData(), 0, Desc->getAllocSize());
     if (Desc->CtorFn)
       Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
                    /*isActive=*/true, Desc);
Index: clang/lib/AST/Interp/Interp.h
===================================================================
--- clang/lib/AST/Interp/Interp.h
+++ clang/lib/AST/Interp/Interp.h
@@ -569,7 +569,10 @@
 
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
-  S.Stk.push<T>(S.Current->getLocal<T>(I));
+  const Pointer &Ptr = S.Current->getLocalPointer(I);
+  if (!CheckLoad(S, OpPC, Ptr))
+    return false;
+  S.Stk.push<T>(Ptr.deref<T>());
   return true;
 }
 
@@ -912,6 +915,8 @@
   const Pointer &Ptr = S.Stk.peek<Pointer>();
   if (!CheckStore(S, OpPC, Ptr))
     return false;
+  if (!Ptr.isRoot())
+    Ptr.initialize();
   Ptr.deref<T>() = Value;
   return true;
 }
@@ -922,6 +927,8 @@
   const Pointer &Ptr = S.Stk.pop<Pointer>();
   if (!CheckStore(S, OpPC, Ptr))
     return false;
+  if (!Ptr.isRoot())
+    Ptr.initialize();
   Ptr.deref<T>() = Value;
   return true;
 }
@@ -932,6 +939,8 @@
   const Pointer &Ptr = S.Stk.peek<Pointer>();
   if (!CheckStore(S, OpPC, Ptr))
     return false;
+  if (!Ptr.isRoot())
+    Ptr.initialize();
   if (auto *FD = Ptr.getField()) {
     Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
   } else {
@@ -946,6 +955,8 @@
   const Pointer &Ptr = S.Stk.pop<Pointer>();
   if (!CheckStore(S, OpPC, Ptr))
     return false;
+  if (!Ptr.isRoot())
+    Ptr.initialize();
   if (auto *FD = Ptr.getField()) {
     Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
   } else {
Index: clang/lib/AST/Interp/EvalEmitter.cpp
===================================================================
--- clang/lib/AST/Interp/EvalEmitter.cpp
+++ clang/lib/AST/Interp/EvalEmitter.cpp
@@ -23,7 +23,8 @@
                          InterpStack &Stk, APValue &Result)
     : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), Result(Result) {
   // Create a dummy frame for the interpreter which does not have locals.
-  S.Current = new InterpFrame(S, nullptr, nullptr, CodePtr(), Pointer());
+  S.Current =
+      new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr());
 }
 
 llvm::Expected<bool> EvalEmitter::interpretExpr(const Expr *E) {
Index: clang/lib/AST/Interp/Descriptor.h
===================================================================
--- clang/lib/AST/Interp/Descriptor.h
+++ clang/lib/AST/Interp/Descriptor.h
@@ -47,6 +47,34 @@
 /// Object size as used by the interpreter.
 using InterpSize = unsigned;
 
+/// Inline descriptor embedded in structures and arrays.
+///
+/// Such descriptors precede all composite array elements and structure fields.
+/// If the base of a pointer is not zero, the base points to the end of this
+/// structure. The offset field is used to traverse the pointer chain up
+/// to the root structure which allocated the object.
+struct InlineDescriptor {
+  /// Offset inside the structure/array.
+  unsigned Offset;
+
+  /// Flag indicating if the storage is constant or not.
+  /// Relevant for primitive fields.
+  unsigned IsConst : 1;
+  /// For primitive fields, it indicates if the field was initialized.
+  /// Primitive fields in static storage are always initialized.
+  /// Arrays are always initialized, even though their elements might not be.
+  /// Base classes are initialized after the constructor is invoked.
+  unsigned IsInitialized : 1;
+  /// Flag indicating if the field is an embedded base class.
+  unsigned IsBase : 1;
+  /// Flag indicating if the field is the active member of a union.
+  unsigned IsActive : 1;
+  /// Flag indicating if the field is mutable (if in a record).
+  unsigned IsMutable : 1;
+
+  Descriptor *Desc;
+};
+
 /// Describes a memory block created by an allocation site.
 struct Descriptor final {
 private:
@@ -56,6 +84,8 @@
   const InterpSize ElemSize;
   /// Size of the storage, in host bytes.
   const InterpSize Size;
+  // Size of the metadata.
+  const InterpSize MDSize;
   /// Size of the allocation (storage + metadata), in host bytes.
   const InterpSize AllocSize;
 
@@ -66,6 +96,9 @@
   /// Token to denote structures of unknown size.
   struct UnknownSize {};
 
+  using MetadataSize = llvm::Optional<InterpSize>;
+  static constexpr MetadataSize InlineDescMD = sizeof(InlineDescriptor);
+
   /// Pointer to the record, if block contains records.
   Record *const ElemRecord = nullptr;
   /// Descriptor of the array element.
@@ -85,26 +118,26 @@
   const BlockMoveFn MoveFn = nullptr;
 
   /// Allocates a descriptor for a primitive.
-  Descriptor(const DeclTy &D, PrimType Type, bool IsConst, bool IsTemporary,
-             bool IsMutable);
+  Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, bool IsConst,
+             bool IsTemporary, bool IsMutable);
 
   /// Allocates a descriptor for an array of primitives.
-  Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, bool IsConst,
-             bool IsTemporary, bool IsMutable);
+  Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, size_t NumElems,
+             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);
 
   /// Allocates a descriptor for an array of composites.
-  Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems, bool IsConst,
-             bool IsTemporary, bool IsMutable);
+  Descriptor(const DeclTy &D, 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);
 
   /// Allocates a descriptor for a record.
-  Descriptor(const DeclTy &D, Record *R, bool IsConst, bool IsTemporary,
-             bool IsMutable);
+  Descriptor(const DeclTy &D, Record *R, MetadataSize MD, bool IsConst,
+             bool IsTemporary, bool IsMutable);
 
   QualType getType() const;
   SourceLocation getLocation() const;
@@ -134,6 +167,8 @@
   unsigned getAllocSize() const { return AllocSize; }
   /// returns the size of an element when the structure is viewed as an array.
   unsigned getElemSize()  const { return ElemSize; }
+  /// Returns the size of the metadata.
+  unsigned getMetadataSize() const { return MDSize; }
 
   /// Returns the number of elements stored in the block.
   unsigned getNumElems() const {
@@ -154,34 +189,6 @@
   bool isArray() const { return IsArray; }
 };
 
-/// Inline descriptor embedded in structures and arrays.
-///
-/// Such descriptors precede all composite array elements and structure fields.
-/// If the base of a pointer is not zero, the base points to the end of this
-/// structure. The offset field is used to traverse the pointer chain up
-/// to the root structure which allocated the object.
-struct InlineDescriptor {
-  /// Offset inside the structure/array.
-  unsigned Offset;
-
-  /// Flag indicating if the storage is constant or not.
-  /// Relevant for primitive fields.
-  unsigned IsConst : 1;
-  /// For primitive fields, it indicates if the field was initialized.
-  /// Primitive fields in static storage are always initialized.
-  /// Arrays are always initialized, even though their elements might not be.
-  /// Base classes are initialized after the constructor is invoked.
-  unsigned IsInitialized : 1;
-  /// Flag indicating if the field is an embedded base class.
-  unsigned IsBase : 1;
-  /// Flag indicating if the field is the active member of a union.
-  unsigned IsActive : 1;
-  /// Flag indicating if the field is mutable (if in a record).
-  unsigned IsMutable : 1;
-
-  Descriptor *Desc;
-};
-
 /// Bitfield tracking the initialisation status of elements of primitive arrays.
 /// A pointer to this is embedded at the end of all primitive arrays.
 /// If the map was not yet created and nothing was initialized, the pointer to
Index: clang/lib/AST/Interp/Descriptor.cpp
===================================================================
--- clang/lib/AST/Interp/Descriptor.cpp
+++ clang/lib/AST/Interp/Descriptor.cpp
@@ -191,19 +191,22 @@
   COMPOSITE_TYPE_SWITCH(Type, return moveArrayTy<T>, return nullptr);
 }
 
-Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsConst,
-                       bool IsTemporary, bool IsMutable)
-    : Source(D), ElemSize(primSize(Type)), Size(ElemSize), AllocSize(Size),
-      IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
-      CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)),
-      MoveFn(getMovePrim(Type)) {
+Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
+                       bool IsConst, bool IsTemporary, bool IsMutable)
+    : Source(D), ElemSize(primSize(Type)), Size(ElemSize),
+      MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), IsConst(IsConst),
+      IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(getCtorPrim(Type)),
+      DtorFn(getDtorPrim(Type)), MoveFn(getMovePrim(Type)) {
+  assert(AllocSize >= Size);
   assert(Source && "Missing source");
 }
 
-Descriptor::Descriptor(const DeclTy &D, PrimType Type, size_t NumElems,
-                       bool IsConst, bool IsTemporary, bool IsMutable)
+Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
+                       size_t NumElems, bool IsConst, bool IsTemporary,
+                       bool IsMutable)
     : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
-      AllocSize(align(Size) + sizeof(InitMap *)), IsConst(IsConst),
+      MDSize(MD.value_or(0)),
+      AllocSize(align(Size) + sizeof(InitMap *) + MDSize), IsConst(IsConst),
       IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true),
       CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
       MoveFn(getMoveArrayPrim(Type)) {
@@ -212,39 +215,42 @@
 
 Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary,
                        UnknownSize)
-    : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark),
+    : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), MDSize(0),
       AllocSize(alignof(void *)), IsConst(true), IsMutable(false),
       IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)),
       DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) {
   assert(Source && "Missing source");
 }
 
-Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems,
-                       bool IsConst, bool IsTemporary, bool IsMutable)
+Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD,
+                       unsigned NumElems, bool IsConst, bool IsTemporary,
+                       bool IsMutable)
     : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
-      Size(ElemSize * NumElems),
-      AllocSize(std::max<size_t>(alignof(void *), Size)), ElemDesc(Elem),
-      IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
-      IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc),
-      MoveFn(moveArrayDesc) {
+      Size(ElemSize * NumElems), MDSize(MD.value_or(0)),
+      AllocSize(std::max<size_t>(alignof(void *), Size) + MDSize),
+      ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable),
+      IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc),
+      DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
   assert(Source && "Missing source");
 }
 
 Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary,
                        UnknownSize)
     : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
-      Size(UnknownSizeMark), AllocSize(alignof(void *)), ElemDesc(Elem),
-      IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
-      CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
+      Size(UnknownSizeMark), MDSize(0), AllocSize(alignof(void *)),
+      ElemDesc(Elem), IsConst(true), IsMutable(false), IsTemporary(IsTemporary),
+      IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc),
+      MoveFn(moveArrayDesc) {
   assert(Source && "Missing source");
 }
 
-Descriptor::Descriptor(const DeclTy &D, Record *R, bool IsConst,
-                       bool IsTemporary, bool IsMutable)
+Descriptor::Descriptor(const DeclTy &D, Record *R, MetadataSize MD,
+                       bool IsConst, bool IsTemporary, bool IsMutable)
     : Source(D), ElemSize(std::max<size_t>(alignof(void *), R->getFullSize())),
-      Size(ElemSize), AllocSize(Size), ElemRecord(R), IsConst(IsConst),
-      IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(ctorRecord),
-      DtorFn(dtorRecord), MoveFn(moveRecord) {
+      Size(ElemSize), MDSize(MD.value_or(0)), AllocSize(Size + MDSize),
+      ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable),
+      IsTemporary(IsTemporary), CtorFn(ctorRecord), DtorFn(dtorRecord),
+      MoveFn(moveRecord) {
   assert(Source && "Missing source");
 }
 
Index: clang/lib/AST/Interp/Context.cpp
===================================================================
--- clang/lib/AST/Interp/Context.cpp
+++ clang/lib/AST/Interp/Context.cpp
@@ -125,7 +125,7 @@
 
 bool Context::Run(State &Parent, Function *Func, APValue &Result) {
   InterpState State(Parent, *P, Stk, *this);
-  State.Current = new InterpFrame(State, Func, nullptr, {}, {});
+  State.Current = new InterpFrame(State, Func, /*Caller=*/nullptr, {});
   if (Interpret(State, Result))
     return true;
   Stk.clear();
Index: clang/lib/AST/Interp/ByteCodeStmtGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -397,19 +397,17 @@
   if (Optional<PrimType> T = this->classify(VD->getType())) {
     const Expr *Init = VD->getInit();
 
-    if (!Init)
-      return false;
-
     unsigned Offset =
         this->allocateLocalPrimitive(VD, *T, VD->getType().isConstQualified());
     // Compile the initializer in its own scope.
-    {
+    if (Init) {
       ExprScope<Emitter> Scope(this);
       if (!this->visit(Init))
         return false;
+
+      return this->emitSetLocal(*T, Offset, VD);
     }
-    // Set the value.
-    return this->emitSetLocal(*T, Offset, VD);
+    return true;
   }
 
   // Composite types - allocate storage and initialize it.
Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -803,7 +803,11 @@
                                                           PrimType Ty,
                                                           bool IsConst,
                                                           bool IsExtended) {
-  Descriptor *D = P.createDescriptor(Src, Ty, IsConst, Src.is<const Expr *>());
+  // FIXME: There are cases where Src.is<Expr*>() is wrong, e.g.
+  //   (int){12} in C. Consider using Expr::isTemporaryObject() instead
+  //   or isa<MaterializeTemporaryExpr>().
+  Descriptor *D = P.createDescriptor(Src, Ty, Descriptor::InlineDescMD, IsConst,
+                                     Src.is<const Expr *>());
   Scope::Local Local = this->createLocal(D);
   if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>()))
     Locals.insert({VD, Local});
@@ -832,7 +836,8 @@
   }
 
   Descriptor *D = P.createDescriptor(
-      Src, Ty.getTypePtr(), Ty.isConstQualified(), IsTemporary, false, Init);
+      Src, Ty.getTypePtr(), Descriptor::InlineDescMD, Ty.isConstQualified(),
+      IsTemporary, /*IsMutable=*/false, Init);
   if (!D)
     return {};
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to