Skip to content

Commit

Permalink
c++: constant non-copy-init is manifestly constant [PR108243]
Browse files Browse the repository at this point in the history
According to [basic.start.static]/2 and [expr.const]/2, a variable
with static storage duration initialized with a constant initializer
has constant initialization, and such an initializer is manifestly
constant-evaluated.

For copy initialization, we're already getting this right because in
that case check_initializer would consistently call store_init_value,
which for TREE_STATIC variables calls fold_non_dependent_init with
m_c_e=true.

But for direct (or default) initialization, check_initializer doesn't
always call store_init_value.  We instead however always call
maybe_constant_init from expand_default_init[1], albeit with m_c_e=false
which means we don't get the "manifestly constant-evaluated" part right
for non-copy-init.

This patch fixes this by setting m_c_e=true in maybe_constant_init for
static storage duration variables, mainly for benefit of the call
to maybe_constant_init from expand_default_init.

[1]: this maybe_constant_init call isn't reached in the copy-init
case because there init is a CONSTRUCTOR rather than a TREE_LIST,
and so we exit early from expand_default_init, returning an INIT_EXPR.
This INIT_EXPR is ultimately what causes us to consistently hit the
store_init_value code path from check_initializer in the copy-init case.

	PR c++/108243

gcc/cp/ChangeLog:

	* constexpr.cc (maybe_constant_init_1): Override
	manifestly_const_eval to true if is_static.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/is-constant-evaluated14.C: New test.
  • Loading branch information
Patrick Palka committed Mar 2, 2023
1 parent 20bd258 commit cbaa1d9
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 0 deletions.
2 changes: 2 additions & 0 deletions gcc/cp/constexpr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8770,6 +8770,8 @@ maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant,
shouldn't bend the rules the same way for automatic variables. */
bool is_static = (decl && DECL_P (decl)
&& (TREE_STATIC (decl) || DECL_EXTERNAL (decl)));
if (is_static)
manifestly_const_eval = true;
t = cxx_eval_outermost_constant_expr (t, allow_non_constant, !is_static,
mce_value (manifestly_const_eval),
false, decl);
Expand Down
140 changes: 140 additions & 0 deletions gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated14.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// PR c++/108243
// Verify a variable with static storage duration initialized with a
// constant initializer has constant initialization, and the initializer
// is manifestly constant-evaluated.
// { dg-do run { target c++11 } }
// { dg-additional-options "-fdump-tree-original" }

// { dg-final { scan-tree-dump-not "static initializers for" "original" } }
// { dg-final { scan-tree-dump-not "cxa_guard_acquire" "original" } }

#include <initializer_list>

struct A {
constexpr A(int n) : n(n), m(__builtin_is_constant_evaluated()) { }
constexpr A() : A(42) { }
void verify_mce() const {
if (m != 1) __builtin_abort();
}
int n;
int m;
};

A a1 = {42};
A a2{42};
A a3(42);
A a4;
A a5{};

void f() {
static A a1 = {42};
static A a2{42};
static A a3(42);
static A a4;
static A a5{};
for (auto& a : {a1, a2, a3, a4, a5})
a.verify_mce();
}

template<int... N>
void g() {
static A a1 = {42};
static A a2{42};
static A a3(42);
static A a4;
static A a5{};
static A a6 = {N...};
static A a7{N...};
static A a8(N...);
for (auto& a : {a1, a2, a3, a4, a5, a6, a7, a8})
a.verify_mce();
}

struct B {
static A a1;
static A a2;
static A a3;
static A a4;
static A a5;
static void verify_mce() {
for (auto& a : {a1, a2, a3, a4, a5})
a.verify_mce();
}
};

A B::a1 = {42};
A B::a2{42};
A B::a3(42);
A B::a4;
A B::a5{};

template<int... N>
struct BT {
static A a1;
static A a2;
static A a3;
static A a4;
static A a5;
static A a6;
static A a7;
static A a8;
static void verify_mce() {
for (auto& a : {a1, a2, a3, a4, a5})
a.verify_mce();
}
};

template<int... N> A BT<N...>::a1 = {42};
template<int... N> A BT<N...>::a2{42};
template<int... N> A BT<N...>::a3(42);
template<int... N> A BT<N...>::a4;
template<int... N> A BT<N...>::a5{};
template<int... N> A BT<N...>::a6 = {N...};
template<int... N> A BT<N...>::a7{N...};
template<int... N> A BT<N...>::a8(N...);

#if __cpp_inline_variables
struct BI {
static inline A a1 = {42};
static inline A a2{42};
static inline A a3;
static inline A a4{};
static void verify_mce() {
for (auto& a : {a1, a2, a3, a4})
a.verify_mce();
}
};

template<int... N>
struct BIT {
static inline A a1 = {42};
static inline A a2{42};
static inline A a3;
static inline A a4{};
static inline A a5 = {N...};
static inline A a6{N...};
static void verify_mce() {
for (auto& a : {a1, a2, a3, a4, a5, a6})
a.verify_mce();
}
};
#endif

int main() {
for (auto& a : {a1, a2, a3, a4, a5})
a.verify_mce();

f();
g<42>();
g<>();

B::verify_mce();
BT<42>::verify_mce();
BT<>::verify_mce();

#if __cpp_inline_variables
BI::verify_mce();
BIT<42>::verify_mce();
BIT<>::verify_mce();
#endif
}

0 comments on commit cbaa1d9

Please sign in to comment.