On 5/6/22 11:22, Patrick Palka wrote:
Here ever since r10-7313-gb599bf9d6d1e18, reduced_constant_expression_p
in C++11/14 is rejecting the marked sub-aggregate initializer (of type S)
W w = {.D.2445={.s={.D.2387={.m=0}, .b=0}}}
^
ultimately because said initializer has CONSTRUCTOR_NO_CLEARING set, and
so the function proceeds to verify that all fields of S are initialized.
And before C++17 we don't expect to see base class fields (since
next_initializable_field skips over the), so the base class initializer
causes r_c_e_p to return false.
That seems like the primary bug. I guess r_c_e_p shouldn't be using
next_initializable_field. Really that function should only be used for
aggregates.
The base class initializer comes from
the constructor call S::S(int).
The reason this is caused by r10-7313-gb599bf9d6d1e18 is because in that
commit we began using CONSTRUCTOR_NO_CLEARING to also track whether we're
in middle of activating a union member. This overloaded use of the flag
affects clear_no_implicit_zero, which recurses into sub-aggregate
initializers only if the outer initializer has CONSTRUCTOR_NO_CLEARING
set.
Is that really overloaded? In both union and non-union cases, it
indicates that the object is not completely initialized.
After that commit, the outer union initializer above no longer has
the flag set at this point and so clear_no_implicit_zero no longer clears
CONSTRUCTOR_NO_CLEARING for the marked inner initializer.
Why wasn't it cleared for the inner initializer as part of that evaluation?
This patch fixes this by restoring the recursive behavior of
clear_no_implicit_zero for union initializers. Arguably we should
we could improve reduced_constant_expression_p to accept the marked
initializer in C++11/14 even if it has CONSTRUCTOR_NO_CLEARING set, but
adjusting clear_no_implicit_zero seems safer to backport.
Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk/12/11/10?
PR c++/105491
gcc/cp/ChangeLog:
* constexpr.cc (clear_no_implicit_zero): Recurse into a union
initializer even if CONSTRUCTOR_NO_CLEARING is already cleared.
gcc/testsuite/ChangeLog:
* g++.dg/cpp0x/constexpr-union7.C: New test.
* g++.dg/cpp0x/constexpr-union7a.C: New test.
* g++.dg/cpp2a/constinit17.C: New test.
---
gcc/cp/constexpr.cc | 7 +++++-
gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C | 17 +++++++++++++
.../g++.dg/cpp0x/constexpr-union7a.C | 15 ++++++++++++
gcc/testsuite/g++.dg/cpp2a/constinit17.C | 24 +++++++++++++++++++
4 files changed, 62 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C
create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-union7a.C
create mode 100644 gcc/testsuite/g++.dg/cpp2a/constinit17.C
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 9b1e71857fc..75fecbcbcb7 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -1886,7 +1886,12 @@ cxx_eval_internal_function (const constexpr_ctx *ctx,
tree t,
static void
clear_no_implicit_zero (tree ctor)
{
- if (CONSTRUCTOR_NO_CLEARING (ctor))
+ if (CONSTRUCTOR_NO_CLEARING (ctor)
+ /* For a union initializer, the flag could already be cleared but not
+ necessarily yet for its sub-aggregates, since for unions the flag is
+ also used by cxx_eval_store_expression to track whether we're in the
+ middle of activating one of its members. */
+ || TREE_CODE (TREE_TYPE (ctor)) == UNION_TYPE)
{
CONSTRUCTOR_NO_CLEARING (ctor) = false;
for (auto &e: CONSTRUCTOR_ELTS (ctor))
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C
new file mode 100644
index 00000000000..b3147d9db50
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-union7.C
@@ -0,0 +1,17 @@
+// PR c++/105491
+// { dg-do compile { target c++11 } }
+
+struct V {
+ int m = 0;
+};
+struct S : V {
+ constexpr S(int) : b() { }
+ bool b;
+};
+struct W {
+ constexpr W() : s(0) { }
+ union {
+ S s;
+ };
+};
+constexpr W w;
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-union7a.C
b/gcc/testsuite/g++.dg/cpp0x/constexpr-union7a.C
new file mode 100644
index 00000000000..b676e7d1748
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-union7a.C
@@ -0,0 +1,15 @@
+// PR c++/105491
+// { dg-do compile { target c++11 } }
+
+struct V {
+ int m = 0;
+};
+struct S : V {
+ constexpr S(int) : b() { }
+ bool b;
+};
+union W {
+ constexpr W() : s(0) { }
+ S s;
+};
+constexpr W w;
diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit17.C
b/gcc/testsuite/g++.dg/cpp2a/constinit17.C
new file mode 100644
index 00000000000..6431654ac85
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/constinit17.C
@@ -0,0 +1,24 @@
+// PR c++/105491
+// { dg-do compile { target c++11 } }
+
+class Message {
+ virtual int GetMetadata();
+};
+class ProtobufCFileOptions : Message {
+public:
+ constexpr ProtobufCFileOptions(int);
+ bool no_generate_;
+ bool const_strings_;
+ bool use_oneof_field_name_;
+ bool gen_pack_helpers_;
+ bool gen_init_helpers_;
+};
+constexpr ProtobufCFileOptions::ProtobufCFileOptions(int)
+ : no_generate_(), const_strings_(), use_oneof_field_name_(),
+ gen_pack_helpers_(), gen_init_helpers_() {}
+struct ProtobufCFileOptionsDefaultTypeInternal {
+ constexpr ProtobufCFileOptionsDefaultTypeInternal() : _instance({}) {}
+ union {
+ ProtobufCFileOptions _instance;
+ };
+} __constinit _ProtobufCFileOptions_default_instance_;