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}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits