[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}}