[clang][bytecode] Check destination size when initializing from an array initlist (#196916)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index faad6e0..beaeed0 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -2205,16 +2205,16 @@
   }
 
   if (QT->isArrayType()) {
-    if (Inits.size() == 1 && QT == Inits[0]->getType())
-      return this->delegate(Inits[0]);
-
     const ConstantArrayType *CAT =
         Ctx.getASTContext().getAsConstantArrayType(QT);
     uint64_t NumElems = CAT->getZExtSize();
 
-    if (!this->emitCheckArraySize(NumElems, E))
+    if (Initializing && !this->emitCheckArrayDestSize(NumElems, E))
       return false;
 
+    if (Inits.size() == 1 && QT == Inits[0]->getType())
+      return this->delegate(Inits[0]);
+
     OptPrimType InitT = classify(CAT->getElementType());
     unsigned ElementIndex = 0;
     for (const Expr *Init : Inits) {
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index fe2d999..b620d1c 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -3753,6 +3753,22 @@
   return true;
 }
 
+/// Check if the destination array we're initializing can hold the \p NumElems
+/// elements.
+inline bool CheckArrayDestSize(InterpState &S, CodePtr OpPC, size_t NumElems) {
+  if (!CheckArraySize(S, OpPC, NumElems))
+    return false;
+
+  const Pointer &Ptr = S.Stk.peek<Pointer>();
+  if (!Ptr.isUnknownSizeArray() && NumElems > Ptr.getNumElems()) {
+    S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_new_too_small)
+        << Ptr.getNumElems() << NumElems;
+    return false;
+  }
+
+  return true;
+}
+
 inline bool Alloc(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
   assert(Desc);
 
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index 57ed71f..0838263 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -458,6 +458,7 @@
 }
 
 def CheckArraySize : Opcode { let Args = [ArgUint64]; }
+def CheckArrayDestSize : Opcode { let Args = [ArgUint64]; }
 def CheckFunctionDecl : Opcode { let Args = [ArgFunctionDecl]; }
 def CheckBitCast : Opcode { let Args = [ArgTypePtr, ArgBool]; }
 
diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp
index ac2c2ff..b6e6d33 100644
--- a/clang/test/AST/ByteCode/new-delete.cpp
+++ b/clang/test/AST/ByteCode/new-delete.cpp
@@ -1197,6 +1197,26 @@
   static_assert(vdtor_3(3) == 3);
 }
 
+namespace ArrayDestSize {
+  template<typename T>
+  constexpr T dynarray(int elems, int i) {
+    T *p;
+    if constexpr (sizeof(T) == 1)
+      p = new T[elems]{"fox"}; // both-note {{evaluated array bound 3 is too small to hold 4 explicitly initialized elements}}
+    else
+      p = new T[elems]{1, 2, 3}; // both-note {{evaluated array bound 2 is too small to hold 3 explicitly initialized elements}}
+    T n = p[i]; // both-note 4{{past-the-end}}
+    delete [] p;
+    return n;
+  }
+  static_assert(dynarray<int>(4, 4) == 0); // both-error {{constant expression}} both-note {{in call}}
+  static_assert(dynarray<int>(3, 3) == 0); // both-error {{constant expression}} both-note {{in call}}
+  static_assert(dynarray<int>(2, 1) == 0); // both-error {{constant expression}} both-note {{in call}}
+  static_assert(dynarray<char>(5, 5) == 0); // both-error {{constant expression}} both-note {{in call}}
+  static_assert(dynarray<char>(4, 4) == 0); // both-error {{constant expression}} both-note {{in call}}
+  static_assert(dynarray<char>(3, 2) == 'x'); // both-error {{constant expression}} both-note {{in call}}
+}
+
 #else
 /// Make sure we reject this prior to C++20
 constexpr int a() { // both-error {{never produces a constant expression}}