cor3ntin updated this revision to Diff 543530. cor3ntin added a comment. Add more Vla tests
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D155955/new/ https://reviews.llvm.org/D155955 Files: clang/docs/ReleaseNotes.rst clang/docs/UsersManual.rst clang/include/clang/AST/Type.h clang/include/clang/Basic/DiagnosticASTKinds.td clang/lib/AST/ExprConstant.cpp clang/lib/AST/Type.cpp clang/test/SemaCXX/cxx2a-constexpr-dynalloc-limits.cpp
Index: clang/test/SemaCXX/cxx2a-constexpr-dynalloc-limits.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/cxx2a-constexpr-dynalloc-limits.cpp @@ -0,0 +1,95 @@ +// RUN: %clang_cc1 -std=c++20 -verify -fconstexpr-steps=1024 -Wvla %s + +namespace std { + using size_t = decltype(sizeof(0)); +} + +void *operator new(std::size_t, void *p) { return p; } + +namespace std { + template<typename T> struct allocator { + constexpr T *allocate(size_t N) { + return (T*)operator new(sizeof(T) * N); // #alloc + } + constexpr void deallocate(void *p) { + operator delete(p); + } + }; + template<typename T, typename ...Args> + constexpr void construct_at(void *p, Args &&...args) { // #construct + new (p) T((Args&&)args...); + } +} + +namespace GH63562 { + +template <typename T> +struct S { + constexpr S(unsigned long long N) + : data(nullptr){ + data = alloc.allocate(N); // #call + for(std::size_t i = 0; i < N; i ++) + std::construct_at<T>(data + i, i); // #construct_call + } + constexpr T operator[](std::size_t i) const { + return data[i]; + } + + constexpr ~S() { + alloc.deallocate(data); + } + std::allocator<T> alloc; + T* data; +}; + +constexpr std::size_t s = S<std::size_t>(1099511627777)[42]; // expected-error {{constexpr variable 's' must be initialized by a constant expression}} \ + // expected-note@#call {{in call to 'this->alloc.allocate(1099511627777)'}} \ + // expected-note@#alloc {{cannot allocate array; evaluated array bound 1099511627777 is too large}} \ + // expected-note {{in call to 'S(1099511627777)'}} +// Check that we do not try to fold very large arrays +std::size_t s2 = S<std::size_t>(1099511627777)[42]; +std::size_t s3 = S<std::size_t>(~0ULL)[42]; + +// We can allocate and initialize a small array +constexpr std::size_t ssmall = S<std::size_t>(100)[42]; + +// We can allocate this array but we hikt the number of steps +constexpr std::size_t s4 = S<std::size_t>(1024)[42]; // expected-error {{constexpr variable 's4' must be initialized by a constant expression}} \ + // expected-note@#construct {{constexpr evaluation hit maximum step limit; possible infinite loop?}} \ + // expected-note@#construct_call {{in call}} \ + // expected-note {{in call}} + + + +constexpr std::size_t s5 = S<std::size_t>(1025)[42]; // expected-error{{constexpr variable 's5' must be initialized by a constant expression}} \ + // expected-note@#alloc {{cannot allocate array; evaluated array bound 1025 exceeds the limit (1024); use '-fconstexpr-steps' to increase this limit}} \ + // expected-note@#call {{in call to 'this->alloc.allocate(1025)'}} \ + // expected-note {{in call}} + + +// Check we do not perform constant initialization in the presence +// of very large arrays (this used to crash) + +template <auto N> +constexpr int stack_array() { + [[maybe_unused]] char BIG[N] = {1}; // expected-note 3{{cannot allocate array; evaluated array bound 1025 exceeds the limit (1024); use '-fconstexpr-steps' to increase this limit}} + return BIG[N-1]; +} + +int a = stack_array<~0U>(); +int c = stack_array<1024>(); +int d = stack_array<1025>(); +constexpr int e = stack_array<1024>(); +constexpr int f = stack_array<1025>(); // expected-error {{constexpr variable 'f' must be initialized by a constant expression}} \ + // expected-note {{in call}} +void ohno() { + int bar[stack_array<1024>()]; + int foo[stack_array<1025>()]; // expected-warning {{variable length arrays are a C99 feature}} \ + // expected-note {{in call to 'stack_array()'}} + + constexpr int foo[stack_array<1025>()]; // expected-warning {{variable length arrays are a C99 feature}} \ + // expected-error {{constexpr variable cannot have non-literal type 'const int[stack_array<1025>()]'}} \ + // expected-note {{in call to 'stack_array()'}} +} + +} Index: clang/lib/AST/Type.cpp =================================================================== --- clang/lib/AST/Type.cpp +++ clang/lib/AST/Type.cpp @@ -175,6 +175,11 @@ return TotalSize.getActiveBits(); } +unsigned +ConstantArrayType::getNumAddressingBits(const ASTContext &Context) const { + return getNumAddressingBits(Context, getElementType(), getSize()); +} + unsigned ConstantArrayType::getMaxSizeBits(const ASTContext &Context) { unsigned Bits = Context.getTypeSize(Context.getSizeType()); Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -1019,6 +1019,34 @@ return false; } + bool CheckArraySize(SourceLocation Loc, unsigned BitWidth, + uint64_t ElemCount, bool Diag) { + // FIXME: GH63562 + // APValue stores array extents as unsigned, + // so anything that is greater that unsigned would overflow when + // constructing the array, we catch this here. + if (BitWidth > ConstantArrayType::getMaxSizeBits(Ctx) || + ElemCount > uint64_t(std::numeric_limits<unsigned>::max())) { + if (Diag) + FFDiag(Loc, diag::note_constexpr_new_too_large) << ElemCount; + return false; + } + + // FIXME: GH63562 + // Arrays allocate an APValue per element. + // We use the number of constexpr steps as a proxy for the maximum size + // of arrays to avoid exhausting the system resources, as initialization + // of each element is likely to take some number of steps anyway. + uint64_t Limit = Ctx.getLangOpts().ConstexprStepLimit; + if (ElemCount > Limit) { + if (Diag) + FFDiag(Loc, diag::note_constexpr_new_exceeds_limits) + << ElemCount << Limit; + return false; + } + return true; + } + std::pair<CallStackFrame *, unsigned> getCallFrameAndDepth(unsigned CallIndex) { assert(CallIndex && "no call index in getCallFrameAndDepth"); @@ -3583,6 +3611,14 @@ llvm_unreachable("unknown evaluating decl kind"); } +static bool CheckArraySize(EvalInfo &Info, const ConstantArrayType *CAT, + SourceLocation CallLoc = {}) { + return Info.CheckArraySize( + CAT->getSizeExpr() ? CAT->getSizeExpr()->getBeginLoc() : CallLoc, + CAT->getNumAddressingBits(Info.Ctx), CAT->getSize().getZExtValue(), + /*Diag=*/true); +} + namespace { /// A handle to a complete object (an object that is not a subobject of /// another object). @@ -3757,6 +3793,9 @@ if (O->getArrayInitializedElts() > Index) O = &O->getArrayInitializedElt(Index); else if (!isRead(handler.AccessKind)) { + if (!CheckArraySize(Info, CAT, E->getExprLoc())) + return handler.failed(); + expandArray(*O, Index); O = &O->getArrayInitializedElt(Index); } else @@ -6491,6 +6530,9 @@ uint64_t Size = CAT->getSize().getZExtValue(); QualType ElemT = CAT->getElementType(); + if (!CheckArraySize(Info, CAT, CallLoc)) + return false; + LValue ElemLV = This; ElemLV.addArray(Info, &LocE, CAT); if (!HandleLValueArrayAdjustment(Info, &LocE, ElemLV, ElemT, Size)) @@ -6681,7 +6723,7 @@ return HandleDestructionImpl(Info, Loc, LV, Value, T); } -/// Perform a call to 'perator new' or to `__builtin_operator_new'. +/// Perform a call to 'operator new' or to `__builtin_operator_new'. static bool HandleOperatorNewCall(EvalInfo &Info, const CallExpr *E, LValue &Result) { if (Info.checkingPotentialConstantExpression() || @@ -6727,13 +6769,12 @@ return false; } - if (ByteSize.getActiveBits() > ConstantArrayType::getMaxSizeBits(Info.Ctx)) { + if (!Info.CheckArraySize(E->getBeginLoc(), ByteSize.getActiveBits(), + Size.getZExtValue(), /*Diag=*/!IsNothrow)) { if (IsNothrow) { Result.setNull(Info.Ctx, E->getType()); return true; } - - Info.FFDiag(E, diag::note_constexpr_new_too_large) << APSInt(Size, true); return false; } @@ -9615,14 +9656,12 @@ // -- its value is such that the size of the allocated object would // exceed the implementation-defined limit - if (ConstantArrayType::getNumAddressingBits(Info.Ctx, AllocType, - ArrayBound) > - ConstantArrayType::getMaxSizeBits(Info.Ctx)) { + if (!Info.CheckArraySize(ArraySize.value()->getExprLoc(), + ConstantArrayType::getNumAddressingBits( + Info.Ctx, AllocType, ArrayBound), + ArrayBound.getZExtValue(), /*Diag=*/!IsNothrow)) { if (IsNothrow) return ZeroInitialization(E); - - Info.FFDiag(*ArraySize, diag::note_constexpr_new_too_large) - << ArrayBound << (*ArraySize)->getSourceRange(); return false; } Index: clang/include/clang/Basic/DiagnosticASTKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticASTKinds.td +++ clang/include/clang/Basic/DiagnosticASTKinds.td @@ -351,6 +351,9 @@ "cannot allocate array; evaluated array bound %0 is negative">; def note_constexpr_new_too_large : Note< "cannot allocate array; evaluated array bound %0 is too large">; +def note_constexpr_new_exceeds_limits : Note< + "cannot allocate array; evaluated array bound %0 exceeds the limit (%1); " + "use '-fconstexpr-steps' to increase this limit">; def note_constexpr_new_too_small : Note< "cannot allocate array; evaluated array bound %0 is too small to hold " "%1 explicitly initialized elements">; Index: clang/include/clang/AST/Type.h =================================================================== --- clang/include/clang/AST/Type.h +++ clang/include/clang/AST/Type.h @@ -3144,6 +3144,8 @@ QualType ElementType, const llvm::APInt &NumElements); + unsigned getNumAddressingBits(const ASTContext &Context) const; + /// Determine the maximum number of active bits that an array's size /// can require, which limits the maximum size of the array. static unsigned getMaxSizeBits(const ASTContext &Context); Index: clang/docs/UsersManual.rst =================================================================== --- clang/docs/UsersManual.rst +++ clang/docs/UsersManual.rst @@ -3348,7 +3348,9 @@ .. option:: -fconstexpr-steps=N Sets the limit for the number of full-expressions evaluated in a single - constant expression evaluation. The default is 1048576. + constant expression evaluation. This also controls the maximum size + of array and dynamic array allocation that can be constant evaluated. + The default is 1048576. .. option:: -ftemplate-depth=N Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -769,10 +769,13 @@ (`#62796 <https://github.com/llvm/llvm-project/issues/62796>`_) - Merge lambdas in require expressions in standard C++ modules. (`#63544 <https://github.com/llvm/llvm-project/issues/63544>`_) - - Fix location of default member initialization in parenthesized aggregate initialization. (`#63903 <https://github.com/llvm/llvm-project/issues/63903>`_) +- Clang no longer tries to evaluate at compile time arrays that are large + enough to cause resource exhaustion, unless they are part of a constant + expression. + (`#63562 <https://github.com/llvm/llvm-project/issues/63562>`_) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits