[clang] 199c791 - [clang][bytecode] Support partial initializers for CXXNewExprs (#126494)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 10 05:28:48 PST 2025
Author: Timm Baeder
Date: 2025-02-10T14:28:40+01:00
New Revision: 199c791a1dbf417fdb08fbbb054d51ed398f285a
URL: https://github.com/llvm/llvm-project/commit/199c791a1dbf417fdb08fbbb054d51ed398f285a
DIFF: https://github.com/llvm/llvm-project/commit/199c791a1dbf417fdb08fbbb054d51ed398f285a.diff
LOG: [clang][bytecode] Support partial initializers for CXXNewExprs (#126494)
For `new A[N]{1,2,3}`, we need to allocate N elements of type A, and
initialize the first three with the given InitListExpr elements.
However, if N is larger than 3, we need to initialize the remaining
elements with the InitListExpr array filler.
Similarly, for `new A[N];`, we need to initilize all fields with the
constructor of A. The initializer type is a CXXConstructExpr of
IncompleteArrayType in this case, which we can't generally handle.
Added:
Modified:
clang/lib/AST/ByteCode/Compiler.cpp
clang/lib/AST/ByteCode/Interp.h
clang/test/AST/ByteCode/new-delete.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 1f0e022edcd7687..86a3773d74d05c5 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -3370,15 +3370,23 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
PrimType SizeT = classifyPrim(Stripped->getType());
+ // Save evaluated array size to a variable.
+ unsigned ArrayLen = allocateLocalPrimitive(
+ Stripped, SizeT, /*IsConst=*/false, /*IsExtended=*/false);
+ if (!this->visit(Stripped))
+ return false;
+ if (!this->emitSetLocal(SizeT, ArrayLen, E))
+ return false;
+
if (PlacementDest) {
if (!this->visit(PlacementDest))
return false;
- if (!this->visit(Stripped))
+ if (!this->emitGetLocal(SizeT, ArrayLen, E))
return false;
if (!this->emitCheckNewTypeMismatchArray(SizeT, E, E))
return false;
} else {
- if (!this->visit(Stripped))
+ if (!this->emitGetLocal(SizeT, ArrayLen, E))
return false;
if (ElemT) {
@@ -3392,10 +3400,113 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
}
}
- if (Init && !this->visitInitializer(Init))
- return false;
+ if (Init) {
+ QualType InitType = Init->getType();
+ size_t StaticInitElems = 0;
+ const Expr *DynamicInit = nullptr;
+ if (const ConstantArrayType *CAT =
+ Ctx.getASTContext().getAsConstantArrayType(InitType)) {
+ StaticInitElems = CAT->getZExtSize();
+ if (!this->visitInitializer(Init))
+ return false;
- } else {
+ if (const auto *ILE = dyn_cast<InitListExpr>(Init);
+ ILE && ILE->hasArrayFiller())
+ DynamicInit = ILE->getArrayFiller();
+ }
+
+ // The initializer initializes a certain number of elements, S.
+ // However, the complete number of elements, N, might be larger than that.
+ // In this case, we need to get an initializer for the remaining elements.
+ // There are to cases:
+ // 1) For the form 'new Struct[n];', the initializer is a
+ // CXXConstructExpr and its type is an IncompleteArrayType.
+ // 2) For the form 'new Struct[n]{1,2,3}', the initializer is an
+ // InitListExpr and the initializer for the remaining elements
+ // is the array filler.
+
+ if (DynamicInit || InitType->isIncompleteArrayType()) {
+ const Function *CtorFunc = nullptr;
+ if (const auto *CE = dyn_cast<CXXConstructExpr>(Init)) {
+ CtorFunc = getFunction(CE->getConstructor());
+ if (!CtorFunc)
+ return false;
+ }
+
+ LabelTy EndLabel = this->getLabel();
+ LabelTy StartLabel = this->getLabel();
+
+ // In the nothrow case, the alloc above might have returned nullptr.
+ // Don't call any constructors that case.
+ if (IsNoThrow) {
+ if (!this->emitDupPtr(E))
+ return false;
+ if (!this->emitNullPtr(0, nullptr, E))
+ return false;
+ if (!this->emitEQPtr(E))
+ return false;
+ if (!this->jumpTrue(EndLabel))
+ return false;
+ }
+
+ // Create loop variables.
+ unsigned Iter = allocateLocalPrimitive(
+ Stripped, SizeT, /*IsConst=*/false, /*IsExtended=*/false);
+ if (!this->emitConst(StaticInitElems, SizeT, E))
+ return false;
+ if (!this->emitSetLocal(SizeT, Iter, E))
+ return false;
+
+ this->fallthrough(StartLabel);
+ this->emitLabel(StartLabel);
+ // Condition. Iter < ArrayLen?
+ if (!this->emitGetLocal(SizeT, Iter, E))
+ return false;
+ if (!this->emitGetLocal(SizeT, ArrayLen, E))
+ return false;
+ if (!this->emitLT(SizeT, E))
+ return false;
+ if (!this->jumpFalse(EndLabel))
+ return false;
+
+ // Pointer to the allocated array is already on the stack.
+ if (!this->emitGetLocal(SizeT, Iter, E))
+ return false;
+ if (!this->emitArrayElemPtr(SizeT, E))
+ return false;
+
+ if (DynamicInit) {
+ if (std::optional<PrimType> InitT = classify(DynamicInit)) {
+ if (!this->visit(DynamicInit))
+ return false;
+ if (!this->emitStorePop(*InitT, E))
+ return false;
+ } else {
+ if (!this->visitInitializer(DynamicInit))
+ return false;
+ if (!this->emitPopPtr(E))
+ return false;
+ }
+ } else {
+ assert(CtorFunc);
+ if (!this->emitCall(CtorFunc, 0, E))
+ return false;
+ }
+
+ // ++Iter;
+ if (!this->emitGetPtrLocal(Iter, E))
+ return false;
+ if (!this->emitIncPop(SizeT, E))
+ return false;
+
+ if (!this->jump(StartLabel))
+ return false;
+
+ this->fallthrough(EndLabel);
+ this->emitLabel(EndLabel);
+ }
+ }
+ } else { // Non-array.
if (PlacementDest) {
if (!this->visit(PlacementDest))
return false;
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 66fd31feb24f4ed..5cc371c7ee49507 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1484,7 +1484,10 @@ bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F,
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) {
const T &Value = S.Stk.pop<T>();
- const Pointer &Field = S.Stk.peek<Pointer>().atField(I);
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+ if (!CheckRange(S, OpPC, Ptr, CSK_Field))
+ return false;
+ const Pointer &Field = Ptr.atField(I);
Field.deref<T>() = Value;
Field.activate();
Field.initialize();
diff --git a/clang/test/AST/ByteCode/new-delete.cpp b/clang/test/AST/ByteCode/new-delete.cpp
index a8f073aa03fc101..e60ff894c9715ae 100644
--- a/clang/test/AST/ByteCode/new-delete.cpp
+++ b/clang/test/AST/ByteCode/new-delete.cpp
@@ -268,11 +268,10 @@ namespace NowThrowNew {
delete[] p;
return result;
}
- /// This needs support for CXXConstrucExprs with non-constant array sizes.
- static_assert(erroneous_array_bound_nothrow2(3)); // expected-error {{not an integral constant expression}}
- static_assert(erroneous_array_bound_nothrow2(0));// expected-error {{not an integral constant expression}}
- static_assert(erroneous_array_bound_nothrow2(-1) == 0);// expected-error {{not an integral constant expression}}
- static_assert(!erroneous_array_bound_nothrow2(1LL << 62));// expected-error {{not an integral constant expression}}
+ static_assert(erroneous_array_bound_nothrow2(3));
+ static_assert(erroneous_array_bound_nothrow2(0));
+ static_assert(erroneous_array_bound_nothrow2(-1) == 0);
+ static_assert(!erroneous_array_bound_nothrow2(1LL << 62));
constexpr bool erroneous_array_bound(long long n) {
delete[] new int[n]; // both-note {{array bound -1 is negative}} both-note {{array bound 4611686018427387904 is too large}}
@@ -857,6 +856,54 @@ struct SS {
};
constexpr unsigned short ssmall = SS<unsigned short>(100)[42];
+
+
+namespace IncompleteArray {
+ struct A {
+ int b = 10;
+ };
+ constexpr int test1() {
+ int n = 5;
+ int* a = new int[n];
+ int c = a[0]; // both-note {{read of uninitialized object}}
+ delete[] a;
+ return c;
+ }
+ static_assert(test1() == 10); // both-error {{not an integral constant expression}} \
+ // both-note {{in call to}}
+
+ constexpr int test2() {
+ int n = 0;
+ int* a = new int[n];
+ delete[] a;
+ return 10;
+ }
+ static_assert(test2() == 10);
+
+ /// In this case, the type of the initializer is A[2], while the full size of the
+ /// allocated array is of course 5. The remaining 3 elements need to be initialized
+ /// using A's constructor.
+ constexpr int test3() {
+ int n = 3;
+ A* a = new A[n]{5, 1};
+ int c = a[0].b + a[1].b + a[2].b;
+ delete[] a;
+ return c;
+ }
+ static_assert(test3() == (5 + 1 + 10));
+
+ constexpr int test4() {
+ auto n = 3;
+ int *a = new int[n]{12};
+ int c = a[0] + a[1];
+ delete[] a;
+ return c;
+ }
+ static_assert(test4() == 12);
+
+
+}
+
#else
/// Make sure we reject this prior to C++20
constexpr int a() { // both-error {{never produces a constant expression}}
More information about the cfe-commits
mailing list