[clang] 9dc4af3 - Re-land "[clang] Add early exit when checking for const init of arrays."
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 26 15:41:45 PST 2023
This change doesn't look correct to me. It's possible to observe an array
during its own initialization, and if that happens, the array needs to be
filled with the array filler up to its declared length. It's not beautiful,
but here's a testcase that was broken by this change:
https://godbolt.org/z/5zof71v36
extern const struct A a;
constexpr const A &get() { return a; }
struct A {
struct B {
constexpr B() : n(get().b[4].n) {}
int n;
};
B b[5];
};
constexpr A a = A();
Looks like it should be relatively straightforward to create the small
version of the array with the filler in place.
On Wed, 29 Dec 2021 at 04:07, Sam McCall via cfe-commits <
cfe-commits at lists.llvm.org> wrote:
>
> Author: Sam McCall
> Date: 2021-12-29T13:07:30+01:00
> New Revision: 9dc4af327b12dfbcf90fde1641cd649c6814bf98
>
> URL:
> https://github.com/llvm/llvm-project/commit/9dc4af327b12dfbcf90fde1641cd649c6814bf98
> DIFF:
> https://github.com/llvm/llvm-project/commit/9dc4af327b12dfbcf90fde1641cd649c6814bf98.diff
>
> LOG: Re-land "[clang] Add early exit when checking for const init of
> arrays."
>
> This reverts commit 6d09aaecdfe51e13fc64d539aa7c9a790de341d7.
>
> The test uses ulimit and ran into problems on some bots. Run on linux only.
> There's nothing platform-specific about the code we're testing, so this
> should be enough to ensure correctness.
>
> Added:
> clang/test/SemaCXX/PR51712-large-array-constexpr-check-oom.cpp
>
> Modified:
> clang/lib/AST/ExprConstant.cpp
>
> Removed:
>
>
>
>
> ################################################################################
> diff --git a/clang/lib/AST/ExprConstant.cpp
> b/clang/lib/AST/ExprConstant.cpp
> index 469339e8cd624..105cd7a3506dd 100644
> --- a/clang/lib/AST/ExprConstant.cpp
> +++ b/clang/lib/AST/ExprConstant.cpp
> @@ -10680,28 +10680,55 @@ bool
> ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E,
> 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;
> + // When checking for const initilization any diagnostic is
> considered
> + // an error.
> + 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
> index 0000000000000..859b1ac3fb2be
> --- /dev/null
> +++ b/clang/test/SemaCXX/PR51712-large-array-constexpr-check-oom.cpp
> @@ -0,0 +1,17 @@
> +// Only run this test where ulimit is known to work well.
> +// (There's nothing really platform-specific being tested, this is just
> ulimit).
> +//
> +// REQUIRES: shell
> +// REQUIRES: linux
> +// UNSUPPORTED: msan
> +// UNSUPPORTED: asan
> +//
> +// RUN: ulimit -v 1048576
> +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -triple=x86_64 %s
> +// expected-no-diagnostics
> +
> +// This used to require too much memory and crash with OOM.
> +struct {
> + int a, b, c, d;
> +} arr[1<<30];
> +
>
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20230126/f438112c/attachment-0001.html>
More information about the cfe-commits
mailing list