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,
hence the function must verify that all fields of S are initialized.
And before C++17 it doesn't expect to see base class fields (since
next_initializable_field skips over them), so the presence thereof
causes r_c_e_p to return false.
The reason
r10-7313-gb599bf9d6d1e18 causes this is because in that
commit we began using CONSTRUCTOR_NO_CLEARING to precisely track whether
we're in middle of activating a union member. This ends up affecting
clear_no_implicit_zero, which recurses into sub-aggregate initializers
only if the outer initializer has CONSTRUCTOR_NO_CLEARING set. 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 recurses into
the marked inner initializer.
But arguably r_c_e_p should be able to accept the marked initializer
regardless of whether CONSTRUCTOR_NO_CLEARING is set. The primary bug
therefore seems to be that r_c_e_p relies on next_initializable_field
which skips over base class fields in C++11/14. To fix this, this patch
introduces a new helper function next_subobject_field which is like
next_initializable_field except that it never skips base class fields,
and makes r_c_e_p use it. This patch then renames next_initializable_field
to next_aggregate_field (and makes it skip over vptr fields again).
NB: This minimal backport of
r13-211-g0c7bce0ac184c0 for 12.2 just adds
next_subobject_field and makes reduced_constant_expression_p use it.
PR c++/105491
gcc/cp/ChangeLog:
* constexpr.cc (reduced_constant_expression_p): Use
next_subobject_field instead.
* cp-tree.h (next_subobject_field): Declare.
* decl.cc (next_subobject_field): Define.
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.
(cherry picked from commit
0c7bce0ac184c057bacad9c8e615ce82923835fd)
field = NULL_TREE;
}
else
- field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t)));
+ field = next_subobject_field (TYPE_FIELDS (TREE_TYPE (t)));
}
else
field = NULL_TREE;
return false;
/* Empty class field may or may not have an initializer. */
for (; field && e.index != field;
- field = next_initializable_field (DECL_CHAIN (field)))
+ field = next_subobject_field (DECL_CHAIN (field)))
if (!is_really_empty_class (TREE_TYPE (field),
/*ignore_vptr*/false))
return false;
if (field)
- field = next_initializable_field (DECL_CHAIN (field));
+ field = next_subobject_field (DECL_CHAIN (field));
}
/* There could be a non-empty field at the end. */
- for (; field; field = next_initializable_field (DECL_CHAIN (field)))
+ for (; field; field = next_subobject_field (DECL_CHAIN (field)))
if (!is_really_empty_class (TREE_TYPE (field), /*ignore_vptr*/false))
return false;
ok:
extern tree check_var_type (tree, tree, location_t);
extern tree reshape_init (tree, tree, tsubst_flags_t);
extern tree next_initializable_field (tree);
+extern tree next_subobject_field (tree);
extern tree first_field (const_tree);
extern tree fndecl_declared_return_type (tree);
extern bool undeduced_auto_decl (tree);
return field;
}
+/* FIELD is an element of TYPE_FIELDS or NULL. In the former case, the value
+ returned is the next FIELD_DECL (possibly FIELD itself) that corresponds
+ to a subobject. If there are no more such fields, the return value will be
+ NULL. */
+
+tree
+next_subobject_field (tree field)
+{
+ while (field
+ && (TREE_CODE (field) != FIELD_DECL
+ || DECL_UNNAMED_BIT_FIELD (field)
+ || (DECL_ARTIFICIAL (field)
+ && !DECL_FIELD_IS_BASE (field)
+ && !DECL_VIRTUAL_P (field))))
+ field = DECL_CHAIN (field);
+
+ return field;
+}
+
/* Return true for [dcl.init.list] direct-list-initialization from
single element of enumeration with a fixed underlying type. */
--- /dev/null
+// 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;
--- /dev/null
+// 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;
--- /dev/null
+// 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_;