c++: designated init and aggregate members [PR103337]

Our C++20 designated initializer handling was broken with members of class
type; we would find the relevant member and then try to find a member of
the member with the same name.  Or we would sometimes ignore the designator
entirely.  The former problem is fixed by the change to reshape_init_class,
the latter by the change to reshape_init_r.

	PR c++/103337
	PR c++/102740
	PR c++/103299
	PR c++/102538

gcc/cp/ChangeLog:

	* decl.cc (reshape_init_class): Avoid looking for designator
	after we found it.
	(reshape_init_r): Keep looking for designator.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/flexary3.C: Remove one error.
	* g++.dg/parse/pr43765.C: Likewise.
	* g++.dg/cpp2a/desig22.C: New test.
	* g++.dg/cpp2a/desig23.C: New test.
	* g++.dg/cpp2a/desig24.C: New test.
	* g++.dg/cpp2a/desig25.C: New test.
This commit is contained in:
Jason Merrill 2022-03-21 09:57:28 -04:00
parent e3e191b410
commit 24d51e7495
7 changed files with 101 additions and 9 deletions

View file

@ -6598,8 +6598,9 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p,
{
tree field_init;
constructor_elt *old_cur = d->cur;
bool direct_desig = false;
/* Handle designated initializers, as an extension. */
/* Handle C++20 designated initializers. */
if (d->cur->index)
{
if (d->cur->index == error_mark_node)
@ -6617,7 +6618,10 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p,
}
}
else if (TREE_CODE (d->cur->index) == IDENTIFIER_NODE)
field = get_class_binding (type, d->cur->index);
{
field = get_class_binding (type, d->cur->index);
direct_desig = true;
}
else
{
if (complain & tf_error)
@ -6669,6 +6673,7 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p,
break;
gcc_assert (aafield);
field = aafield;
direct_desig = false;
}
}
@ -6683,9 +6688,32 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p,
assumed to correspond to no elements of the initializer list. */
goto continue_;
field_init = reshape_init_r (TREE_TYPE (field), d,
/*first_initializer_p=*/NULL_TREE,
complain);
if (direct_desig)
{
/* The designated field F is initialized from this one element:
Temporarily clear the designator so a recursive reshape_init_class
doesn't try to find it again in F, and adjust d->end so we don't
try to use the next initializer to initialize another member of F.
Note that we don't want these changes if we found the designator
inside an anon aggr above; we leave them alone to implement:
"If the element is an anonymous union member and the initializer
list is a brace-enclosed designated- initializer-list, the element
is initialized by the designated-initializer-list { D }, where D
is the designated- initializer-clause naming a member of the
anonymous union member." */
auto end_ = make_temp_override (d->end, d->cur + 1);
auto idx_ = make_temp_override (d->cur->index, NULL_TREE);
field_init = reshape_init_r (TREE_TYPE (field), d,
/*first_initializer_p=*/NULL_TREE,
complain);
}
else
field_init = reshape_init_r (TREE_TYPE (field), d,
/*first_initializer_p=*/NULL_TREE,
complain);
if (field_init == error_mark_node)
return error_mark_node;
@ -6941,6 +6969,15 @@ reshape_init_r (tree type, reshape_iter *d, tree first_initializer_p,
to handle initialization of arrays and similar. */
else if (COMPOUND_LITERAL_P (stripped_init))
gcc_assert (!BRACE_ENCLOSED_INITIALIZER_P (stripped_init));
/* If we have an unresolved designator, we need to find the member it
designates within TYPE, so proceed to the routines below. For
FIELD_DECL or INTEGER_CST designators, we're already initializing
the designated element. */
else if (d->cur->index
&& TREE_CODE (d->cur->index) == IDENTIFIER_NODE)
/* Brace elision with designators is only permitted for anonymous
aggregates. */
gcc_checking_assert (ANON_AGGR_TYPE_P (type));
/* A CONSTRUCTOR of the target's type is a previously
digested initializer. */
else if (same_type_ignoring_top_level_qualifiers_p (type, init_type))

View file

@ -0,0 +1,11 @@
// PR c++/103337
// { dg-do compile { target c++20 } }
struct op_t {
struct put_t {
int x;
} put;
};
op_t x{0}; // OK
op_t y{.put=0}; // bogus error: 'op_t::put_t' has no non-static data member named 'put'

View file

@ -0,0 +1,20 @@
// PR c++/102740
// { dg-do compile { target c++20 } }
// { dg-additional-options -Wmissing-braces }
typedef struct {
union {
struct {
const void* content;
} put;
};
} op_t;
op_t f(const char* alias) {
return op_t{
.put =
{
.content = alias,
},
}; // { dg-warning "missing braces" }
}

View file

@ -0,0 +1,11 @@
// PR c++/103299
// { dg-do compile { target c++20 } }
struct foo {
union {
int fp1{};
char fp2;
};
};
static_assert(foo{.fp2={}}.fp2 == 0);

View file

@ -0,0 +1,13 @@
// PR c++/102538
// { dg-do run { target c++20 } }
struct X { union { char r8[8]; int r32[2]; }; };
struct Y { X v[1]; };
Y x = { { { .r32 = { 5, 6 } } } };
int
main ()
{
if (x.v[0].r32[0] != 5 || x.v[0].r32[1] != 6)
__builtin_abort ();
}

View file

@ -16,7 +16,7 @@ struct s {
int main()
{
struct s s = { .c = 0 }; // { dg-error "initializer" }
struct s s = { .c = 0 };
// { dg-error "non-static initialization of a flexible array member" "" { target *-*-* } .-1 }
return 0;
}

View file

@ -12,6 +12,6 @@ SomeType vals[] =
{
{ 0, values : temp, }, // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++2a } }
0
}; // { dg-error "GNU-style designated initializer for an array|cannot convert" }
// (note the error above is on the wrong line)
// { dg-error "initialization of flexible array member in a nested context" "" { target *-*-* } .-2 }
};
// (note the error below is on the wrong line)
// { dg-error "initialization of flexible array member in a nested context" "" { target *-*-* } .-2 }