-
Notifications
You must be signed in to change notification settings - Fork 10.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Clang] Improve the handling of large arrays evaluation.
This is a temporary fix (for clang 17) that caps the size of any array we try to constant evaluate: There are 2 limits: * We cap to UINT_MAX the size of ant constant evaluated array, because the constant evaluator does not support size_t. * We cap to `-fconstexpr-steps` elements the size of each individual array and dynamic array allocations. This works out because the number of constexpr steps already limits how many array elements can be initialized, which makes this new limit conservatively generous. This ensure that the compiler does not crash when attempting to constant-fold valid programs. If the limit is reached by a given array, constant evaluation will fail, and the program will be ill-formed, until a bigger limit is given. Or, constant folding will fail and the array will be evaluated at runtime. Fixes #63562 Reviewed By: efriedma Differential Revision: https://reviews.llvm.org/D155955
- Loading branch information
Showing
7 changed files
with
162 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()'}} | ||
} | ||
|
||
} |