On Sat, Mar 02, 2019 at 10:30:36AM +0100, Jakub Jelinek wrote: > I'm not really sure what to do for foo. Perhaps if we find that case just > require that the order is ok already during build_aggr_conv and fail the > conversion otherwise? We are outside of the standard in that case anyway.
Actually, seems if the designators can match corresponding type, reshape_init* already fills in the ce->index even on elements without original designators. So, the following works fine for all the testcases I came up with. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2019-03-04 Jakub Jelinek <ja...@redhat.com> PR c++/71446 * call.c (field_in_pset): New function. (build_aggr_conv): Handle CONSTRUCTOR_IS_DESIGNATED_INIT correctly. * g++.dg/cpp2a/desig12.C: New test. * g++.dg/cpp2a/desig13.C: New test. --- gcc/cp/call.c.jj 2019-03-02 09:05:44.442524338 +0100 +++ gcc/cp/call.c 2019-03-02 18:57:12.221904541 +0100 @@ -902,6 +902,28 @@ can_convert_array (tree atype, tree ctor return true; } +/* Helper for build_aggr_conv. Return true if FIELD is in PSET, or if + FIELD has ANON_AGGR_TYPE_P and any initializable field in there recursively + is in PSET. */ + +static bool +field_in_pset (hash_set<tree> *pset, tree field) +{ + if (pset->contains (field)) + return true; + if (ANON_AGGR_TYPE_P (TREE_TYPE (field))) + for (field = TYPE_FIELDS (TREE_TYPE (field)); + field; field = DECL_CHAIN (field)) + { + field = next_initializable_field (field); + if (field == NULL_TREE) + break; + if (field_in_pset (pset, field)) + return true; + } + return false; +} + /* Represent a conversion from CTOR, a braced-init-list, to TYPE, an aggregate class, if such a conversion is possible. */ @@ -912,6 +934,7 @@ build_aggr_conv (tree type, tree ctor, i conversion *c; tree field = next_initializable_field (TYPE_FIELDS (type)); tree empty_ctor = NULL_TREE; + hash_set<tree> *pset = NULL; /* We already called reshape_init in implicit_conversion. */ @@ -919,26 +942,69 @@ build_aggr_conv (tree type, tree ctor, i context; they're always simple copy-initialization. */ flags = LOOKUP_IMPLICIT|LOOKUP_NO_NARROWING; + /* For designated initializers, verify that each initializer is convertible + to corresponding TREE_TYPE (ce->index) and mark those FIELD_DECLs as + visited. In the following loop then ignore already visited + FIELD_DECLs. */ + if (CONSTRUCTOR_IS_DESIGNATED_INIT (ctor)) + { + tree idx, val; + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), i, idx, val) + { + if (idx && TREE_CODE (idx) == FIELD_DECL) + { + tree ftype = TREE_TYPE (idx); + bool ok; + + if (TREE_CODE (ftype) == ARRAY_TYPE + && TREE_CODE (val) == CONSTRUCTOR) + ok = can_convert_array (ftype, val, flags, complain); + else + ok = can_convert_arg (ftype, TREE_TYPE (val), val, flags, + complain); + + if (!ok) + goto fail; + /* For unions, there should be just one initializer. */ + if (TREE_CODE (type) == UNION_TYPE) + { + field = NULL_TREE; + i = 1; + break; + } + if (pset == NULL) + pset = new hash_set<tree>; + pset->add (idx); + } + else + goto fail; + } + } + for (; field; field = next_initializable_field (DECL_CHAIN (field))) { tree ftype = TREE_TYPE (field); tree val; bool ok; + if (pset && field_in_pset (pset, field)) + continue; if (i < CONSTRUCTOR_NELTS (ctor)) - val = CONSTRUCTOR_ELT (ctor, i)->value; + { + val = CONSTRUCTOR_ELT (ctor, i)->value; + ++i; + } else if (DECL_INITIAL (field)) val = get_nsdmi (field, /*ctor*/false, complain); else if (TYPE_REF_P (ftype)) /* Value-initialization of reference is ill-formed. */ - return NULL; + goto fail; else { if (empty_ctor == NULL_TREE) empty_ctor = build_constructor (init_list_type_node, NULL); val = empty_ctor; } - ++i; if (TREE_CODE (ftype) == ARRAY_TYPE && TREE_CODE (val) == CONSTRUCTOR) @@ -948,15 +1014,22 @@ build_aggr_conv (tree type, tree ctor, i complain); if (!ok) - return NULL; + goto fail; if (TREE_CODE (type) == UNION_TYPE) break; } if (i < CONSTRUCTOR_NELTS (ctor)) - return NULL; + { + fail: + if (pset) + delete pset; + return NULL; + } + if (pset) + delete pset; c = alloc_conversion (ck_aggr); c->type = type; c->rank = cr_exact; --- gcc/testsuite/g++.dg/cpp2a/desig12.C.jj 2019-03-02 18:58:26.300704919 +0100 +++ gcc/testsuite/g++.dg/cpp2a/desig12.C 2019-03-02 18:59:29.033689025 +0100 @@ -0,0 +1,15 @@ +// PR c++/71446 +// { dg-do compile { target c++11 } } +// { dg-options "" } + +struct T { void *a; int b; }; +struct U { int a; union { int b; union { long c; short d; }; }; int e; }; +void bar (T); +void baz (U); + +void +foo () +{ + bar ({.b = 1}); + baz ({.c = 5L, .e = 6}); +} --- gcc/testsuite/g++.dg/cpp2a/desig13.C.jj 2019-03-02 19:00:33.812639999 +0100 +++ gcc/testsuite/g++.dg/cpp2a/desig13.C 2019-03-02 19:03:48.043494642 +0100 @@ -0,0 +1,16 @@ +// PR c++/71446 +// { dg-do compile { target c++11 } } +// { dg-options "" } + +struct S { int a, b, c, d, e; }; +struct T { int a, b; }; +void foo (S); +void bar (T); + +void +baz () +{ + foo ({.d = 5, 6, .b = 2, 3}); // { dg-error "designator order for field 'S::b' does not match declaration order in 'S'" } + // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++2a } .-1 } + bar ({.b = 1, .a = 2}); // { dg-error "designator order for field 'T::a' does not match declaration order in 'T'" } +} Jakub