diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -10596,28 +10596,53 @@ bool HadZeroInit = Value->hasValue(); if (const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(Type)) { - unsigned N = CAT->getSize().getZExtValue(); + unsigned FinalSize = CAT->getSize().getZExtValue(); // Preserve the array filler if we had prior zero-initialization. APValue Filler = HadZeroInit && Value->hasArrayFiller() ? Value->getArrayFiller() : APValue(); - *Value = APValue(APValue::UninitArray(), N, N); - - if (HadZeroInit) - for (unsigned I = 0; I != N; ++I) - Value->getArrayInitializedElt(I) = Filler; + *Value = APValue(APValue::UninitArray(), 0, FinalSize); + if (FinalSize == 0) + return true; - // Initialize the elements. LValue ArrayElt = Subobject; ArrayElt.addArray(Info, E, CAT); - for (unsigned I = 0; I != N; ++I) - if (!VisitCXXConstructExpr(E, ArrayElt, &Value->getArrayInitializedElt(I), - CAT->getElementType()) || - !HandleLValueArrayAdjustment(Info, E, ArrayElt, - CAT->getElementType(), 1)) - return false; + // We do the whole initialization in two passes, first for just one element, + // then for the whole array. It's possible we may find out we can't do const + // init in the first pass, in which case we avoid allocating a potentially + // large array. We don't do more passes because expanding array requires + // copying the data, which is wasteful. + for (const unsigned N : {1u, FinalSize}) { + unsigned OldElts = Value->getArrayInitializedElts(); + if (OldElts == N) + break; + + // Expand the array to appropriate size. + APValue NewValue(APValue::UninitArray(), N, FinalSize); + for (unsigned I = 0; I < OldElts; ++I) + NewValue.getArrayInitializedElt(I).swap( + Value->getArrayInitializedElt(I)); + Value->swap(NewValue); + + if (HadZeroInit) + for (unsigned I = OldElts; I < N; ++I) + Value->getArrayInitializedElt(I) = Filler; + + // Initialize the elements. + for (unsigned I = OldElts; I < N; ++I) { + if (!VisitCXXConstructExpr(E, ArrayElt, + &Value->getArrayInitializedElt(I), + CAT->getElementType()) || + !HandleLValueArrayAdjustment(Info, E, ArrayElt, + CAT->getElementType(), 1)) + return false; + if (Info.EvalStatus.Diag && !Info.EvalStatus.Diag->empty() && + !Info.keepEvaluatingAfterFailure()) + return false; + } + } return true; } diff --git a/clang/test/SemaCXX/PR51712-large-array-constexpr-check-oom.cpp b/clang/test/SemaCXX/PR51712-large-array-constexpr-check-oom.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/PR51712-large-array-constexpr-check-oom.cpp @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s +// expected-no-diagnostics + +// This used to require too much memory and crash with OOM. +struct { + int a, b, c, d; +} arr[1<<30]; +