Skip to content

Commit c471e51

Browse files
tbaederrIcohedron
authored andcommitted
[clang][bytecode] Support partial initializers for CXXNewExprs (llvm#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.
1 parent 8cfcb74 commit c471e51

File tree

3 files changed

+172
-11
lines changed

3 files changed

+172
-11
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 116 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3370,15 +3370,23 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
33703370

33713371
PrimType SizeT = classifyPrim(Stripped->getType());
33723372

3373+
// Save evaluated array size to a variable.
3374+
unsigned ArrayLen = allocateLocalPrimitive(
3375+
Stripped, SizeT, /*IsConst=*/false, /*IsExtended=*/false);
3376+
if (!this->visit(Stripped))
3377+
return false;
3378+
if (!this->emitSetLocal(SizeT, ArrayLen, E))
3379+
return false;
3380+
33733381
if (PlacementDest) {
33743382
if (!this->visit(PlacementDest))
33753383
return false;
3376-
if (!this->visit(Stripped))
3384+
if (!this->emitGetLocal(SizeT, ArrayLen, E))
33773385
return false;
33783386
if (!this->emitCheckNewTypeMismatchArray(SizeT, E, E))
33793387
return false;
33803388
} else {
3381-
if (!this->visit(Stripped))
3389+
if (!this->emitGetLocal(SizeT, ArrayLen, E))
33823390
return false;
33833391

33843392
if (ElemT) {
@@ -3392,10 +3400,113 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
33923400
}
33933401
}
33943402

3395-
if (Init && !this->visitInitializer(Init))
3396-
return false;
3403+
if (Init) {
3404+
QualType InitType = Init->getType();
3405+
size_t StaticInitElems = 0;
3406+
const Expr *DynamicInit = nullptr;
3407+
if (const ConstantArrayType *CAT =
3408+
Ctx.getASTContext().getAsConstantArrayType(InitType)) {
3409+
StaticInitElems = CAT->getZExtSize();
3410+
if (!this->visitInitializer(Init))
3411+
return false;
33973412

3398-
} else {
3413+
if (const auto *ILE = dyn_cast<InitListExpr>(Init);
3414+
ILE && ILE->hasArrayFiller())
3415+
DynamicInit = ILE->getArrayFiller();
3416+
}
3417+
3418+
// The initializer initializes a certain number of elements, S.
3419+
// However, the complete number of elements, N, might be larger than that.
3420+
// In this case, we need to get an initializer for the remaining elements.
3421+
// There are to cases:
3422+
// 1) For the form 'new Struct[n];', the initializer is a
3423+
// CXXConstructExpr and its type is an IncompleteArrayType.
3424+
// 2) For the form 'new Struct[n]{1,2,3}', the initializer is an
3425+
// InitListExpr and the initializer for the remaining elements
3426+
// is the array filler.
3427+
3428+
if (DynamicInit || InitType->isIncompleteArrayType()) {
3429+
const Function *CtorFunc = nullptr;
3430+
if (const auto *CE = dyn_cast<CXXConstructExpr>(Init)) {
3431+
CtorFunc = getFunction(CE->getConstructor());
3432+
if (!CtorFunc)
3433+
return false;
3434+
}
3435+
3436+
LabelTy EndLabel = this->getLabel();
3437+
LabelTy StartLabel = this->getLabel();
3438+
3439+
// In the nothrow case, the alloc above might have returned nullptr.
3440+
// Don't call any constructors that case.
3441+
if (IsNoThrow) {
3442+
if (!this->emitDupPtr(E))
3443+
return false;
3444+
if (!this->emitNullPtr(0, nullptr, E))
3445+
return false;
3446+
if (!this->emitEQPtr(E))
3447+
return false;
3448+
if (!this->jumpTrue(EndLabel))
3449+
return false;
3450+
}
3451+
3452+
// Create loop variables.
3453+
unsigned Iter = allocateLocalPrimitive(
3454+
Stripped, SizeT, /*IsConst=*/false, /*IsExtended=*/false);
3455+
if (!this->emitConst(StaticInitElems, SizeT, E))
3456+
return false;
3457+
if (!this->emitSetLocal(SizeT, Iter, E))
3458+
return false;
3459+
3460+
this->fallthrough(StartLabel);
3461+
this->emitLabel(StartLabel);
3462+
// Condition. Iter < ArrayLen?
3463+
if (!this->emitGetLocal(SizeT, Iter, E))
3464+
return false;
3465+
if (!this->emitGetLocal(SizeT, ArrayLen, E))
3466+
return false;
3467+
if (!this->emitLT(SizeT, E))
3468+
return false;
3469+
if (!this->jumpFalse(EndLabel))
3470+
return false;
3471+
3472+
// Pointer to the allocated array is already on the stack.
3473+
if (!this->emitGetLocal(SizeT, Iter, E))
3474+
return false;
3475+
if (!this->emitArrayElemPtr(SizeT, E))
3476+
return false;
3477+
3478+
if (DynamicInit) {
3479+
if (std::optional<PrimType> InitT = classify(DynamicInit)) {
3480+
if (!this->visit(DynamicInit))
3481+
return false;
3482+
if (!this->emitStorePop(*InitT, E))
3483+
return false;
3484+
} else {
3485+
if (!this->visitInitializer(DynamicInit))
3486+
return false;
3487+
if (!this->emitPopPtr(E))
3488+
return false;
3489+
}
3490+
} else {
3491+
assert(CtorFunc);
3492+
if (!this->emitCall(CtorFunc, 0, E))
3493+
return false;
3494+
}
3495+
3496+
// ++Iter;
3497+
if (!this->emitGetPtrLocal(Iter, E))
3498+
return false;
3499+
if (!this->emitIncPop(SizeT, E))
3500+
return false;
3501+
3502+
if (!this->jump(StartLabel))
3503+
return false;
3504+
3505+
this->fallthrough(EndLabel);
3506+
this->emitLabel(EndLabel);
3507+
}
3508+
}
3509+
} else { // Non-array.
33993510
if (PlacementDest) {
34003511
if (!this->visit(PlacementDest))
34013512
return false;

clang/lib/AST/ByteCode/Interp.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1484,7 +1484,10 @@ bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F,
14841484
template <PrimType Name, class T = typename PrimConv<Name>::T>
14851485
bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) {
14861486
const T &Value = S.Stk.pop<T>();
1487-
const Pointer &Field = S.Stk.peek<Pointer>().atField(I);
1487+
const Pointer &Ptr = S.Stk.peek<Pointer>();
1488+
if (!CheckRange(S, OpPC, Ptr, CSK_Field))
1489+
return false;
1490+
const Pointer &Field = Ptr.atField(I);
14881491
Field.deref<T>() = Value;
14891492
Field.activate();
14901493
Field.initialize();

clang/test/AST/ByteCode/new-delete.cpp

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,10 @@ namespace NowThrowNew {
268268
delete[] p;
269269
return result;
270270
}
271-
/// This needs support for CXXConstrucExprs with non-constant array sizes.
272-
static_assert(erroneous_array_bound_nothrow2(3)); // expected-error {{not an integral constant expression}}
273-
static_assert(erroneous_array_bound_nothrow2(0));// expected-error {{not an integral constant expression}}
274-
static_assert(erroneous_array_bound_nothrow2(-1) == 0);// expected-error {{not an integral constant expression}}
275-
static_assert(!erroneous_array_bound_nothrow2(1LL << 62));// expected-error {{not an integral constant expression}}
271+
static_assert(erroneous_array_bound_nothrow2(3));
272+
static_assert(erroneous_array_bound_nothrow2(0));
273+
static_assert(erroneous_array_bound_nothrow2(-1) == 0);
274+
static_assert(!erroneous_array_bound_nothrow2(1LL << 62));
276275

277276
constexpr bool erroneous_array_bound(long long n) {
278277
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 {
857856
};
858857
constexpr unsigned short ssmall = SS<unsigned short>(100)[42];
859858

859+
860+
861+
namespace IncompleteArray {
862+
struct A {
863+
int b = 10;
864+
};
865+
constexpr int test1() {
866+
int n = 5;
867+
int* a = new int[n];
868+
int c = a[0]; // both-note {{read of uninitialized object}}
869+
delete[] a;
870+
return c;
871+
}
872+
static_assert(test1() == 10); // both-error {{not an integral constant expression}} \
873+
// both-note {{in call to}}
874+
875+
constexpr int test2() {
876+
int n = 0;
877+
int* a = new int[n];
878+
delete[] a;
879+
return 10;
880+
}
881+
static_assert(test2() == 10);
882+
883+
/// In this case, the type of the initializer is A[2], while the full size of the
884+
/// allocated array is of course 5. The remaining 3 elements need to be initialized
885+
/// using A's constructor.
886+
constexpr int test3() {
887+
int n = 3;
888+
A* a = new A[n]{5, 1};
889+
int c = a[0].b + a[1].b + a[2].b;
890+
delete[] a;
891+
return c;
892+
}
893+
static_assert(test3() == (5 + 1 + 10));
894+
895+
constexpr int test4() {
896+
auto n = 3;
897+
int *a = new int[n]{12};
898+
int c = a[0] + a[1];
899+
delete[] a;
900+
return c;
901+
}
902+
static_assert(test4() == 12);
903+
904+
905+
}
906+
860907
#else
861908
/// Make sure we reject this prior to C++20
862909
constexpr int a() { // both-error {{never produces a constant expression}}

0 commit comments

Comments
 (0)