c++: constexpr init of union sub-aggr w/ base [PR105491]

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).

	PR c++/105491

gcc/cp/ChangeLog:

	* call.cc (field_in_pset): Adjust after next_initializable_field
	renaming.
	(build_aggr_conv): Likewise.
	(convert_like_internal): Likewise.
	(type_has_extended_temps): Likewise.
	* class.cc (default_init_uninitialized_part): Likewise.
	(finish_struct): Likewise.
	* constexpr.cc (cx_check_missing_mem_inits): Likewise.
	(reduced_constant_expression_p): Use next_subobject_field
	instead.
	* cp-gimplify.cc (get_source_location_impl_type): Adjust after
	next_initializable_field renaming.
	(fold_builtin_source_location): Likewise.
	* cp-tree.h (next_initializable_field): Rename to ...
	(next_aggregate_field): ... this.
	(next_subobject_field): Declare.
	* decl.cc (next_aggregate_field): Renamed from ...
	(next_initializable_field): ... this.  Skip over vptr fields
	again.
	(next_subobject_field): Define.
	(reshape_init_class): Adjust after next_initializable_field
	renaming.
	* init.cc (build_value_init_noctor): Likewise.
	(emit_mem_initializers): Likewise.
	* lambda.cc (build_capture_proxy): Likewise.
	* method.cc (build_comparison_op): Likewise.
	* pt.cc (maybe_aggr_guide): Likewise.
	* tree.cc (structural_type_p): Likewise.
	* typeck2.cc (split_nonconstant_init_1): Likewise.
	(digest_init_r): Likewise.

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.
This commit is contained in:
Patrick Palka 2022-05-09 09:53:27 -04:00
parent fcda0efcca
commit 0c7bce0ac1
15 changed files with 116 additions and 43 deletions

View file

@ -948,7 +948,7 @@ field_in_pset (hash_set<tree, true> &pset, tree field)
for (field = TYPE_FIELDS (TREE_TYPE (field)); for (field = TYPE_FIELDS (TREE_TYPE (field));
field; field = DECL_CHAIN (field)) field; field = DECL_CHAIN (field))
{ {
field = next_initializable_field (field); field = next_aggregate_field (field);
if (field == NULL_TREE) if (field == NULL_TREE)
break; break;
if (field_in_pset (pset, field)) if (field_in_pset (pset, field))
@ -965,7 +965,7 @@ build_aggr_conv (tree type, tree ctor, int flags, tsubst_flags_t complain)
{ {
unsigned HOST_WIDE_INT i = 0; unsigned HOST_WIDE_INT i = 0;
conversion *c; conversion *c;
tree field = next_initializable_field (TYPE_FIELDS (type)); tree field = next_aggregate_field (TYPE_FIELDS (type));
tree empty_ctor = NULL_TREE; tree empty_ctor = NULL_TREE;
hash_set<tree, true> pset; hash_set<tree, true> pset;
@ -1011,7 +1011,7 @@ build_aggr_conv (tree type, tree ctor, int flags, tsubst_flags_t complain)
} }
} }
for (; field; field = next_initializable_field (DECL_CHAIN (field))) for (; field; field = next_aggregate_field (DECL_CHAIN (field)))
{ {
tree ftype = TREE_TYPE (field); tree ftype = TREE_TYPE (field);
tree val; tree val;
@ -8098,10 +8098,10 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum,
totype = complete_type_or_maybe_complain (totype, NULL_TREE, complain); totype = complete_type_or_maybe_complain (totype, NULL_TREE, complain);
if (!totype) if (!totype)
return error_mark_node; return error_mark_node;
tree field = next_initializable_field (TYPE_FIELDS (totype)); tree field = next_aggregate_field (TYPE_FIELDS (totype));
vec<constructor_elt, va_gc> *vec = NULL; vec<constructor_elt, va_gc> *vec = NULL;
CONSTRUCTOR_APPEND_ELT (vec, field, array); CONSTRUCTOR_APPEND_ELT (vec, field, array);
field = next_initializable_field (DECL_CHAIN (field)); field = next_aggregate_field (DECL_CHAIN (field));
CONSTRUCTOR_APPEND_ELT (vec, field, size_int (len)); CONSTRUCTOR_APPEND_ELT (vec, field, size_int (len));
tree new_ctor = build_constructor (totype, vec); tree new_ctor = build_constructor (totype, vec);
return get_target_expr_sfinae (new_ctor, complain); return get_target_expr_sfinae (new_ctor, complain);
@ -13267,8 +13267,8 @@ type_has_extended_temps (tree type)
{ {
if (is_std_init_list (type)) if (is_std_init_list (type))
return true; return true;
for (tree f = next_initializable_field (TYPE_FIELDS (type)); for (tree f = next_aggregate_field (TYPE_FIELDS (type));
f; f = next_initializable_field (DECL_CHAIN (f))) f; f = next_aggregate_field (DECL_CHAIN (f)))
if (type_has_extended_temps (TREE_TYPE (f))) if (type_has_extended_temps (TREE_TYPE (f)))
return true; return true;
} }

View file

@ -5494,8 +5494,8 @@ default_init_uninitialized_part (tree type)
if (r) if (r)
return r; return r;
} }
for (t = next_initializable_field (TYPE_FIELDS (type)); t; for (t = next_aggregate_field (TYPE_FIELDS (type)); t;
t = next_initializable_field (DECL_CHAIN (t))) t = next_aggregate_field (DECL_CHAIN (t)))
if (!DECL_INITIAL (t) && !DECL_ARTIFICIAL (t)) if (!DECL_INITIAL (t) && !DECL_ARTIFICIAL (t))
{ {
r = default_init_uninitialized_part (TREE_TYPE (t)); r = default_init_uninitialized_part (TREE_TYPE (t));
@ -7781,10 +7781,10 @@ finish_struct (tree t, tree attributes)
bool ok = false; bool ok = false;
if (processing_template_decl) if (processing_template_decl)
{ {
tree f = next_initializable_field (TYPE_FIELDS (t)); tree f = next_aggregate_field (TYPE_FIELDS (t));
if (f && TYPE_PTR_P (TREE_TYPE (f))) if (f && TYPE_PTR_P (TREE_TYPE (f)))
{ {
f = next_initializable_field (DECL_CHAIN (f)); f = next_aggregate_field (DECL_CHAIN (f));
if (f && same_type_p (TREE_TYPE (f), size_type_node)) if (f && same_type_p (TREE_TYPE (f), size_type_node))
ok = true; ok = true;
} }

View file

@ -784,7 +784,7 @@ cx_check_missing_mem_inits (tree ctype, tree body, bool complain)
if (TREE_CODE (ctype) == UNION_TYPE) if (TREE_CODE (ctype) == UNION_TYPE)
{ {
if (nelts == 0 && next_initializable_field (field)) if (nelts == 0 && next_aggregate_field (field))
{ {
if (complain) if (complain)
error ("%<constexpr%> constructor for union %qT must " error ("%<constexpr%> constructor for union %qT must "
@ -3053,7 +3053,7 @@ reduced_constant_expression_p (tree t)
field = NULL_TREE; field = NULL_TREE;
} }
else else
field = next_initializable_field (TYPE_FIELDS (TREE_TYPE (t))); field = next_subobject_field (TYPE_FIELDS (TREE_TYPE (t)));
} }
else else
field = NULL_TREE; field = NULL_TREE;
@ -3065,15 +3065,15 @@ reduced_constant_expression_p (tree t)
return false; return false;
/* Empty class field may or may not have an initializer. */ /* Empty class field may or may not have an initializer. */
for (; field && e.index != field; 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), if (!is_really_empty_class (TREE_TYPE (field),
/*ignore_vptr*/false)) /*ignore_vptr*/false))
return false; return false;
if (field) 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. */ /* 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)) if (!is_really_empty_class (TREE_TYPE (field), /*ignore_vptr*/false))
return false; return false;
ok: ok:

View file

@ -3106,7 +3106,7 @@ get_source_location_impl_type (location_t loc)
int cnt = 0; int cnt = 0;
for (tree field = TYPE_FIELDS (type); for (tree field = TYPE_FIELDS (type);
(field = next_initializable_field (field)) != NULL_TREE; (field = next_aggregate_field (field)) != NULL_TREE;
field = DECL_CHAIN (field)) field = DECL_CHAIN (field))
{ {
if (DECL_NAME (field) != NULL_TREE) if (DECL_NAME (field) != NULL_TREE)
@ -3281,7 +3281,7 @@ fold_builtin_source_location (location_t loc)
vec<constructor_elt, va_gc> *v = NULL; vec<constructor_elt, va_gc> *v = NULL;
vec_alloc (v, 4); vec_alloc (v, 4);
for (tree field = TYPE_FIELDS (source_location_impl); for (tree field = TYPE_FIELDS (source_location_impl);
(field = next_initializable_field (field)) != NULL_TREE; (field = next_aggregate_field (field)) != NULL_TREE;
field = DECL_CHAIN (field)) field = DECL_CHAIN (field))
{ {
const char *n = IDENTIFIER_POINTER (DECL_NAME (field)); const char *n = IDENTIFIER_POINTER (DECL_NAME (field));

View file

@ -6870,7 +6870,8 @@ extern bool is_direct_enum_init (tree, tree);
extern void initialize_artificial_var (tree, vec<constructor_elt, va_gc> *); extern void initialize_artificial_var (tree, vec<constructor_elt, va_gc> *);
extern tree check_var_type (tree, tree, location_t); extern tree check_var_type (tree, tree, location_t);
extern tree reshape_init (tree, tree, tsubst_flags_t); extern tree reshape_init (tree, tree, tsubst_flags_t);
extern tree next_initializable_field (tree); extern tree next_aggregate_field (tree);
extern tree next_subobject_field (tree);
extern tree first_field (const_tree); extern tree first_field (const_tree);
extern tree fndecl_declared_return_type (tree); extern tree fndecl_declared_return_type (tree);
extern bool undeduced_auto_decl (tree); extern bool undeduced_auto_decl (tree);

View file

@ -6384,20 +6384,36 @@ static tree reshape_init_r (tree, reshape_iter *, tree, tsubst_flags_t);
/* FIELD is an element of TYPE_FIELDS or NULL. In the former case, the value /* 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 can be returned is the next FIELD_DECL (possibly FIELD itself) that can be
initialized. If there are no more such fields, the return value initialized as if for an aggregate class. If there are no more such fields,
will be NULL. */ the return value will be NULL. */
tree tree
next_initializable_field (tree field) next_aggregate_field (tree field)
{ {
while (field while (field
&& (TREE_CODE (field) != FIELD_DECL && (TREE_CODE (field) != FIELD_DECL
|| DECL_UNNAMED_BIT_FIELD (field) || DECL_UNNAMED_BIT_FIELD (field)
|| (DECL_ARTIFICIAL (field) || (DECL_ARTIFICIAL (field)
/* In C++17, don't skip base class fields. */ /* In C++17, aggregates can have bases. */
&& !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field)) && !(cxx_dialect >= cxx17 && DECL_FIELD_IS_BASE (field)))))
/* Don't skip vptr fields. We might see them when we're field = DECL_CHAIN (field);
called from reduced_constant_expression_p. */
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)))) && !DECL_VIRTUAL_P (field))))
field = DECL_CHAIN (field); field = DECL_CHAIN (field);
@ -6595,7 +6611,7 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p,
if (base_binfo) if (base_binfo)
field = base_binfo; field = base_binfo;
else else
field = next_initializable_field (TYPE_FIELDS (type)); field = next_aggregate_field (TYPE_FIELDS (type));
if (!field) if (!field)
{ {
@ -6762,10 +6778,10 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p,
if (BINFO_BASE_ITERATE (binfo, ++binfo_idx, base_binfo)) if (BINFO_BASE_ITERATE (binfo, ++binfo_idx, base_binfo))
field = base_binfo; field = base_binfo;
else else
field = next_initializable_field (TYPE_FIELDS (type)); field = next_aggregate_field (TYPE_FIELDS (type));
} }
else else
field = next_initializable_field (DECL_CHAIN (field)); field = next_aggregate_field (DECL_CHAIN (field));
} }
/* A trailing aggregate element that is a pack expansion is assumed to /* A trailing aggregate element that is a pack expansion is assumed to

View file

@ -422,7 +422,7 @@ build_value_init_noctor (tree type, tsubst_flags_t complain)
&& !COMPLETE_TYPE_P (ftype) && !COMPLETE_TYPE_P (ftype)
&& !TYPE_DOMAIN (ftype) && !TYPE_DOMAIN (ftype)
&& COMPLETE_TYPE_P (TREE_TYPE (ftype)) && COMPLETE_TYPE_P (TREE_TYPE (ftype))
&& (next_initializable_field (DECL_CHAIN (field)) && (next_aggregate_field (DECL_CHAIN (field))
== NULL_TREE)) == NULL_TREE))
continue; continue;
@ -1477,9 +1477,9 @@ emit_mem_initializers (tree mem_inits)
/* Initially that is all of them. */ /* Initially that is all of them. */
if (warn_uninitialized) if (warn_uninitialized)
for (tree f = next_initializable_field (TYPE_FIELDS (current_class_type)); for (tree f = next_aggregate_field (TYPE_FIELDS (current_class_type));
f != NULL_TREE; f != NULL_TREE;
f = next_initializable_field (DECL_CHAIN (f))) f = next_aggregate_field (DECL_CHAIN (f)))
if (!DECL_ARTIFICIAL (f) if (!DECL_ARTIFICIAL (f)
&& !is_really_empty_class (TREE_TYPE (f), /*ignore_vptr*/false)) && !is_really_empty_class (TREE_TYPE (f), /*ignore_vptr*/false))
uninitialized.add (f); uninitialized.add (f);

View file

@ -425,9 +425,9 @@ build_capture_proxy (tree member, tree init)
if (DECL_VLA_CAPTURE_P (member)) if (DECL_VLA_CAPTURE_P (member))
{ {
/* Rebuild the VLA type from the pointer and maxindex. */ /* Rebuild the VLA type from the pointer and maxindex. */
tree field = next_initializable_field (TYPE_FIELDS (type)); tree field = next_aggregate_field (TYPE_FIELDS (type));
tree ptr = build_simple_component_ref (object, field); tree ptr = build_simple_component_ref (object, field);
field = next_initializable_field (DECL_CHAIN (field)); field = next_aggregate_field (DECL_CHAIN (field));
tree max = build_simple_component_ref (object, field); tree max = build_simple_component_ref (object, field);
type = build_cplus_array_type (TREE_TYPE (TREE_TYPE (ptr)), type = build_cplus_array_type (TREE_TYPE (TREE_TYPE (ptr)),
build_index_type (max)); build_index_type (max));

View file

@ -1465,7 +1465,7 @@ build_comparison_op (tree fndecl, bool defining, tsubst_flags_t complain)
/* A defaulted comparison operator function for class C is defined as /* A defaulted comparison operator function for class C is defined as
deleted if ... C has variant members. */ deleted if ... C has variant members. */
if (TREE_CODE (ctype) == UNION_TYPE if (TREE_CODE (ctype) == UNION_TYPE
&& next_initializable_field (TYPE_FIELDS (ctype))) && next_aggregate_field (TYPE_FIELDS (ctype)))
{ {
if (complain & tf_error) if (complain & tf_error)
inform (info.loc, "cannot default compare union %qT", ctype); inform (info.loc, "cannot default compare union %qT", ctype);
@ -1518,9 +1518,9 @@ build_comparison_op (tree fndecl, bool defining, tsubst_flags_t complain)
} }
/* Now compare the field subobjects. */ /* Now compare the field subobjects. */
for (tree field = next_initializable_field (TYPE_FIELDS (ctype)); for (tree field = next_aggregate_field (TYPE_FIELDS (ctype));
field; field;
field = next_initializable_field (DECL_CHAIN (field))) field = next_aggregate_field (DECL_CHAIN (field)))
{ {
if (DECL_VIRTUAL_P (field) || DECL_FIELD_IS_BASE (field)) if (DECL_VIRTUAL_P (field) || DECL_FIELD_IS_BASE (field))
/* We ignore the vptr, and we already handled bases. */ /* We ignore the vptr, and we already handled bases. */
@ -1542,7 +1542,7 @@ build_comparison_op (tree fndecl, bool defining, tsubst_flags_t complain)
continue; continue;
} }
else if (ANON_UNION_TYPE_P (expr_type) else if (ANON_UNION_TYPE_P (expr_type)
&& next_initializable_field (TYPE_FIELDS (expr_type))) && next_aggregate_field (TYPE_FIELDS (expr_type)))
{ {
if (complain & tf_error) if (complain & tf_error)
inform (field_loc, "cannot default compare " inform (field_loc, "cannot default compare "

View file

@ -29586,7 +29586,7 @@ maybe_aggr_guide (tree tmpl, tree init, vec<tree,va_gc> *args)
len; len;
--len, field = DECL_CHAIN (field)) --len, field = DECL_CHAIN (field))
{ {
field = next_initializable_field (field); field = next_aggregate_field (field);
if (!field) if (!field)
return NULL_TREE; return NULL_TREE;
tree ftype = finish_decltype_type (field, true, complain); tree ftype = finish_decltype_type (field, true, complain);

View file

@ -4852,8 +4852,8 @@ structural_type_p (tree t, bool explain)
explain_non_literal_class (t); explain_non_literal_class (t);
return false; return false;
} }
for (tree m = next_initializable_field (TYPE_FIELDS (t)); m; for (tree m = next_aggregate_field (TYPE_FIELDS (t)); m;
m = next_initializable_field (DECL_CHAIN (m))) m = next_aggregate_field (DECL_CHAIN (m)))
{ {
if (TREE_PRIVATE (m) || TREE_PROTECTED (m)) if (TREE_PRIVATE (m) || TREE_PROTECTED (m))
{ {

View file

@ -606,7 +606,7 @@ split_nonconstant_init_1 (tree dest, tree init, bool last,
: TYPE_FIELDS (type)); : TYPE_FIELDS (type));
; prev = DECL_CHAIN (prev)) ; prev = DECL_CHAIN (prev))
{ {
prev = next_initializable_field (prev); prev = next_aggregate_field (prev);
if (prev == field_index) if (prev == field_index)
break; break;
tree ptype = TREE_TYPE (prev); tree ptype = TREE_TYPE (prev);
@ -1304,7 +1304,7 @@ digest_init_r (tree type, tree init, int nested, int flags,
the first element of d, which is the B base subobject. The base the first element of d, which is the B base subobject. The base
of type B is copy-initialized from the D temporary, causing of type B is copy-initialized from the D temporary, causing
object slicing. */ object slicing. */
tree field = next_initializable_field (TYPE_FIELDS (type)); tree field = next_aggregate_field (TYPE_FIELDS (type));
if (field && DECL_FIELD_IS_BASE (field)) if (field && DECL_FIELD_IS_BASE (field))
{ {
if (warning_at (loc, 0, "initializing a base class of type %qT " if (warning_at (loc, 0, "initializing a base class of type %qT "

View file

@ -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;

View file

@ -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;

View file

@ -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_;