Local constexpr variables and static storage duration

907 views
Skip to first unread message

Columbo

unread,
Nov 2, 2015, 5:04:51 PM11/2/15
to ISO C++ Standard - Future Proposals
Is it possible to let local variables declared with constexpr have static storage duration? Since every constexpr variables's initializer can neither depend upon parameter values nor dynamically initialized (or modifiable) global state, they may as well have static storage duration with static initialization AFAICS.  Would that introduce any problems or have significant impacts? Note that it would allow reasonable things that are currently disallowed, e.g.
void f() {
    constexpr int arr[] {1, 2, 3}; 
    constexpr auto p = arr;
}

David Krauss

unread,
Nov 2, 2015, 10:52:57 PM11/2/15
to std-pr...@isocpp.org
On 2015–11–03, at 6:04 AM, Columbo <r....@gmx.net> wrote:

Is it possible to let local variables declared with constexpr have static storage duration? Since every constexpr variables's initializer can neither depend upon parameter values nor dynamically initialized (or modifiable) global state, they may as well have static storage duration with static initialization AFAICS.  

Static storage and initialization aren’t necessarily better. The scheme uses less space while a function is recursing (it has at least two active stack frames), but potentially more space when it’s not executing. It may also reduce cache locality.

Would that introduce any problems or have significant impacts?

It would violate object uniqueness.

On the other hand, this is an area where some flexibility might be found. Already, sequence elements in std::initializer_list arrays are de-facto not guaranteed uniqueness. If it’s treated as an optional optimization (outside the as-if rule) instead of a requirement,  then your proposal more-or-less comes down to allocating constexpr local variables as if they were inside initializer lists.

Separately but similarly, I think it would be nice to un-uniquefy all prvalue expressions (except when bound to non-const references). In any case, I’m not aware of much practical value to static storage outside of large tables. This is the problem domain of initializer_list, however, it has its own issues.

Note that it would allow reasonable things that are currently disallowed, e.g.
void f() {
    constexpr int arr[] {1, 2, 3}; 
    constexpr auto p = arr;
}

This works on GCC:

constexpr std::initializer_list< int > il = { 1, 2, 3 };
constexpr int const * p = il.begin();

Clang rejects this, I guess because the initializer_list stores a pointer to the first element of the array and not a pointer to the entire array object, and because it validates the “permitted result of a constant expression” rule before applying the static storage optimization. I think that both are conforming, or at least, GCC is being reasonable :P .

Of course, for cases such as this where static storage is required, you should simply declare the variable as static. With that adjustment, both of our examples work portably.

Columbo

unread,
Nov 3, 2015, 4:07:21 AM11/3/15
to ISO C++ Standard - Future Proposals
On Tuesday, November 3, 2015 at 3:52:57 AM UTC, David Krauss wrote:
The scheme uses less space while a function is recursing (it has at least two active stack frames), but potentially more space when it’s not executing. It may also reduce cache locality.
Do implementations not elide code and static local variables for functions that are never called? As a link-time optimization?
 
On the other hand, this is an area where some flexibility might be found. Already, sequence elements in std::initializer_list arrays are de-facto not guaranteed uniqueness.
How is that? All these sequence elements are copied into a const array, and [intro.object]/6 guarantees that the elements have distinct addresses. If some of them were overlapping, that would not give the desired behavior for iteration.

Note that it would allow reasonable things that are currently disallowed, e.g.
void f() {
    constexpr int arr[] {1, 2, 3}; 
    constexpr auto p = arr;
}

This works on GCC:

constexpr std::initializer_list< int > il = { 1, 2, 3 };
constexpr int const * p = il.begin();

Clang rejects this, I guess because the initializer_list stores a pointer to the first element of the array and not a pointer to the entire array object, and because it validates the “permitted result of a constant expression” rule before applying the static storage optimization. I think that both are conforming, or at least, GCC is being reasonable :P.
The array held is temporary, thus not having static storage duration. Hence the "permitted result" rule is guaranteed to apply, AFAICS. 

Columbo

unread,
Nov 3, 2015, 4:15:52 AM11/3/15
to ISO C++ Standard - Future Proposals
On Tuesday, November 3, 2015 at 9:07:21 AM UTC, Columbo wrote:
The array held is temporary, thus not having static storage duration. Hence the "permitted result" rule is guaranteed to apply, AFAICS. 
Sorry, I have to rectify that first sentence: The array does not have static storage duration unless the initializer_list does. So actually, in namespace scope, your program should compile.

David Krauss

unread,
Nov 3, 2015, 9:12:35 AM11/3/15
to std-pr...@isocpp.org

On 2015–11–03, at 5:07 PM, Columbo <r....@gmx.net> wrote:

On Tuesday, November 3, 2015 at 3:52:57 AM UTC, David Krauss wrote:
The scheme uses less space while a function is recursing (it has at least two active stack frames), but potentially more space when it’s not executing. It may also reduce cache locality.
Do implementations not elide code and static local variables for functions that are never called? As a link-time optimization?

Yes, but that’s something else. I’m talking about a function which isn’t executing at the moment, not one which is completely unused.

On the other hand, this is an area where some flexibility might be found. Already, sequence elements in std::initializer_list arrays are de-facto not guaranteed uniqueness.
How is that? All these sequence elements are copied into a const array, and [intro.object]/6 guarantees that the elements have distinct addresses. If some of them were overlapping, that would not give the desired behavior for iteration.

The arrays of two initializer_list objects may not be distinct, despite [dcl.init.list] §8.5.4/5-6 specifying one array per list object. The elements in the array are sequential and distinct.
To be clear, my example was at function scope, as yours was. The initializer_list static array storage optimization is not strictly conforming, but the intent was always to allow it, and all the implementations, to my knowledge, do it. That’s why I called it “de-facto.” If you find this confusing, feel free to submit a defect report.

Reply all
Reply to author
Forward
0 new messages