[PATCH] c++, v4: implement C++29 P2287R6 - Designated-initializers for Base Classes [PR125989]
Jakub Jelinek <[email protected]> Wed, 5 Aug 2026 13:11:17 +0200
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <anMaVSHKaYQo4nRj@tucnak> |
On Tue, Aug 04, 2026 at 09:46:21PM -0400, Jason Merrill wrote:
> Nice.
>
> But it seems to me that once we cache the lookups this way, we don't need to
> determine the set of designators that belong to the current base; we should
> be able to look them all up and let the normal handling work from there.
>
> I felt awkward about continuing to ask you for changes, so I poked at it
> some myself. The first patch changes reshape_init_class to avoid changing
> d->end, and I think makes sense to combine with your patch.
Thanks. I've looked at your first patch and it looks correct to me and indeed
simplifies stuff.
> It would also be good to check -Wmissing-braces in at least one of the new
> testcases.
I've added -Wmissing-braces coverage into desig14.C now, see incremental
diff here and attached full patch (my last patch + your simplify patch
+ this incremental).
So far tested with
GXX_TESTSUITE_STDS=98,11,14,17,20,23,26,29 make check-g++ RUNTESTFLAGS="dg.exp='desig* feat-cxx29.C pr43765.C embed-14.C'"
together, ok for trunk if it passes full bootstrap/regtest?
> The second patch changes _class to go through reshape_init_r for this case
> instead of directly recursing into reshape_init_class, which avoids the
> duplication of -Wmissing-braces handling. This required more adjustment of
> reshape_init_r than I expected, so I'm inclined to make it a followup, but I
> think it's a useful clarification.
Not sure if I understood all the details but I haven't tried much yet.
If it can go in separately, doesn't have to be done immediately.
--- gcc/testsuite/g++.dg/cpp29/desig14.C.jj 2026-08-05 12:33:10.574200452 +0200
+++ gcc/testsuite/g++.dg/cpp29/desig14.C 2026-08-05 12:57:26.171939454 +0200
@@ -1,14 +1,23 @@
// C++29 P2287R6 - Designated-initializers for Base Classes
// { dg-do compile { target c++20 } }
-// { dg-options "" }
+// { dg-options "-Wmissing-braces" }
struct A { int a, b; };
struct B { int c, d; };
struct C : A, B { int e, f; };
+struct D : C { int g; };
auto c1 = C { { .a = 1, .b = 2 }, { .c = 3, .d = 4 }, .e = 5, .f = 6 }; // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } }
auto c2 = C { { .a = 1, .b = 2 }, .e = 5, .f = 6 }; // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } }
auto c3 = C { {}, { .c = 3, .d = 4 }, .f = 6 }; // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } }
auto c4 = C { .e = 1, 2 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } }
// { dg-error "designated initializer clause should not be followed by non-designated" "" { target c++29 } .-1 }
+auto c5 = C { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6 }; // { dg-error "'C' has no non-static data member named 'a'" "" { target c++26_down } }
+ // { dg-warning "missing braces around initializer for 'A'" "" { target c++29 } .-1 }
+ // { dg-warning "missing braces around initializer for 'B'" "" { target c++29 } .-2 }
auto a1 = A { 1, .b = 2 }; // { dg-error "last non-designated initializer clause does not appertain to a base class subobject" }
// { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } .-1 }
+auto d1 = D { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, .g = 7 }; // { dg-error "'D' has no non-static data member named 'a'" "" { target c++26_down } }
+ // { dg-warning "missing braces around initializer for 'A'" "" { target c++29 } .-1 }
+ // { dg-warning "missing braces around initializer for 'B'" "" { target c++29 } .-2 }
+ // { dg-warning "missing braces around initializer for 'C'" "" { target c++29 } .-3 }
+auto d2 = D { { { .a = 1, .b = 2 }, { .c = 3, .d = 4 }, .e = 5, .f = 6 }, .g = 7 }; // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } }
Jakub
P581
(text/plain, 38.3 KB)
2026-08-05 Jakub Jelinek <[email protected]> Jason Merrill <[email protected]> PR c++/125989 gcc/c-family/ * c-cppbuiltin.cc (c_cpp_builtins): Predefine __cpp_designated_initializers to 202606 instead of 201707 for C++29. gcc/cp/ * parser.cc: Implement C++29 P2287R6 - Designated-initializers for Base Classes. (cp_parser_initializer_list): For C++29, allow non-designated clauses followed by designated ones, provided designators are identifiers. For C++20-26, allow that as a pedwarned about extension but use the C++20 wording in that case. * decl.cc (reshape_init_class): Diagnose non-designated clause not appertaining to base class followed by designated clause for C++ >= 20. For C++29, if get_class_binding for designator is unsuccessful, use lookup_member afterwards and handle the case where the lookup succeeds for some member of a base class through recursive reshape_init_class. gcc/testsuite/ * g++.dg/cpp/embed-14.C: Expect different diagnostics for C++29 and sometimes for C++20-C++26 too. * g++.dg/cpp2a/desig2.C: Likewise. * g++.dg/cpp2a/desig13.C: Likewise. * g++.dg/cpp2a/desig20.C: Likewise. * g++.dg/ext/desig4.C: Likewise. * g++.dg/parse/pr43765.C: Likewise. * g++.dg/cpp29/feat-cxx29.C: Expect different value of __cpp_designated_initializers for C++29. * g++.dg/cpp29/desig1.C: New test. * g++.dg/cpp29/desig2.C: New test. * g++.dg/cpp29/desig3.C: New test. * g++.dg/cpp29/desig4.C: New test. * g++.dg/cpp29/desig5.C: New test. * g++.dg/cpp29/desig6.C: New test. * g++.dg/cpp29/desig7.C: New test. * g++.dg/cpp29/desig8.C: New test. * g++.dg/cpp29/desig9.C: New test. * g++.dg/cpp29/desig10.C: New test. * g++.dg/cpp29/desig11.C: New test. * g++.dg/cpp29/desig12.C: New test. * g++.dg/cpp29/desig13.C: New test. * g++.dg/cpp29/desig14.C: New test. * g++.dg/cpp29/desig15.C: New test. * g++.dg/cpp29/desig16.C: New test. --- gcc/c-family/c-cppbuiltin.cc.jj 2026-07-27 12:45:24.349871210 +0200 +++ gcc/c-family/c-cppbuiltin.cc 2026-08-05 12:33:10.560785823 +0200 @@ -1072,7 +1072,8 @@ c_cpp_builtins (cpp_reader *pfile) /* Set feature test macros for C++20. */ cpp_define (pfile, "__cpp_init_captures=201803L"); cpp_define (pfile, "__cpp_generic_lambdas=201707L"); - cpp_define (pfile, "__cpp_designated_initializers=201707L"); + if (cxx_dialect <= cxx26) + cpp_define (pfile, "__cpp_designated_initializers=201707L"); if (cxx_dialect <= cxx20) cpp_define (pfile, "__cpp_constexpr=202002L"); cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L"); @@ -1129,6 +1130,7 @@ c_cpp_builtins (cpp_reader *pfile) { /* Set feature test macros for C++29. */ cpp_define (pfile, "__cpp_pp_embed=202606L"); + cpp_define (pfile, "__cpp_designated_initializers=202606L"); } if (flag_concepts && cxx_dialect > cxx14) cpp_define (pfile, "__cpp_concepts=202002L"); --- gcc/cp/parser.cc.jj 2026-08-04 10:27:31.735878042 +0200 +++ gcc/cp/parser.cc 2026-08-05 12:33:10.566785749 +0200 @@ -29408,13 +29408,35 @@ cp_parser_initializer_list (cp_parser* p } else if (cxx_dialect >= cxx20 && first_designator != error_mark_node - && (!first_designator != !designator)) + && (!first_designator != !designator) + && (cxx_dialect < cxx29 + || first_designator + || TREE_CODE (designator) != IDENTIFIER_NODE)) { - error_at (loc, "either all initializer clauses should be designated " - "or none of them should be"); - first_designator = error_mark_node; + if (cxx_dialect < cxx29 + && !first_designator + && TREE_CODE (designator) == IDENTIFIER_NODE) + { + pedwarn (loc, OPT_Wc__29_extensions, + "either all initializer clauses should be " + "designated or none of them should be"); + first_designator = designator; + } + else + { + if (cxx_dialect < cxx29 + || TREE_CODE (first_designator + ? first_designator + : designator) != IDENTIFIER_NODE) + error_at (loc, "either all initializer clauses should be " + "designated or none of them should be"); + else + error_at (loc, "designated initializer clause should not " + "be followed by non-designated"); + first_designator = error_mark_node; + } } - else if (cxx_dialect < cxx20 && !first_designator) + else if (!first_designator) first_designator = designator; /* Parse the initializer. */ --- gcc/cp/decl.cc.jj 2026-07-27 12:45:24.350871197 +0200 +++ gcc/cp/decl.cc 2026-08-05 12:33:50.858291920 +0200 @@ -7829,8 +7829,25 @@ reshape_init_class (tree type, reshape_i return new_init; } + /* For C++29 designated initializers we do modify d->cur->index in place + to cache name lookup results. Make sure to undo it before returning. */ + struct designator_undo { + constructor_elt *start, *end; + void undo () + { + while (start != end) + { + start->index = DECL_NAME (start->index); + ++start; + } + start = end = nullptr; + } + ~designator_undo () { undo (); } + } desig_undo = { nullptr, nullptr }; + /* For C++20 CTAD, handle pack expansions in the base list. */ tree last_was_pack_expansion = NULL_TREE; + bool first_desig = true; /* Loop through the initializable fields, gathering initializers. */ while (d->cur != d->end) @@ -7839,6 +7856,7 @@ reshape_init_class (tree type, reshape_i constructor_elt *old_cur = d->cur; unsigned old_raw_idx = d->raw_idx; bool direct_desig = false; + bool subclass = false; /* Handle C++20 designated initializers. */ if (d->cur->index) @@ -7848,22 +7866,37 @@ reshape_init_class (tree type, reshape_i if (TREE_CODE (d->cur->index) == FIELD_DECL) { - /* We already reshaped this; we should have returned early from - reshape_init. */ - gcc_checking_assert (false); - if (field != d->cur->index) - { - if (tree id = DECL_NAME (d->cur->index)) - gcc_checking_assert (d->cur->index - == get_class_binding (type, id)); - field = d->cur->index; - } + CONSTRUCTOR_IS_DESIGNATED_INIT (new_init) = true; + direct_desig = true; + field = d->cur->index; } else if (TREE_CODE (d->cur->index) == IDENTIFIER_NODE) { + if (first_desig && cxx_dialect >= cxx20) + { + if (CONSTRUCTOR_NELTS (new_init)) + { + constructor_elt *last + = &CONSTRUCTOR_ELTS (new_init)->last (); + if (last->index == NULL_TREE + || TREE_CODE (last->index) != FIELD_DECL + || !DECL_FIELD_IS_BASE (last->index)) + { + if (complain & tf_error) + error ("last non-designated initializer clause " + "does not appertain to a base class " + "subobject"); + return error_mark_node; + } + } + first_desig = false; + } CONSTRUCTOR_IS_DESIGNATED_INIT (new_init) = true; field = get_class_binding (type, d->cur->index); direct_desig = true; + if (!field && cxx_dialect >= cxx29) + field = lookup_member (type, d->cur->index, /*protect=*/2, + /*want_type=*/false, complain); } else { @@ -7913,6 +7946,62 @@ reshape_init_class (tree type, reshape_i ictx = cctx; } + /* In C++29 a designator can name a member of a base; in that + case, go through the designators and replace ids with _DECLs + to record the lookup for the most-derived class. */ + if (cxx_dialect >= cxx29) + { + tree ibinfo = lookup_base (type, ictx, ba_unique, NULL, + complain); + if (!ibinfo) + /* The designator names a field outside this base class, + so we're done. */ + break; + else if (ibinfo != error_mark_node) + { + while (BINFO_INHERITANCE_CHAIN (ibinfo) != binfo) + ibinfo = BINFO_INHERITANCE_CHAIN (ibinfo); + ictx = TREE_TYPE (ibinfo); + + desig_undo.undo (); + + if (d->cur->index != field) + { + d->cur->index = field; + desig_undo.start = d->cur; + } + constructor_elt *e = d->cur + 1; + for (; e != d->end; ++e) + { + if (e->index == NULL_TREE + || e->index == error_mark_node) + break; + if (desig_undo.start) + { + gcc_assert (TREE_CODE (e->index) + == IDENTIFIER_NODE); + field = lookup_member (type, e->index, + /*protect=*/2, + /*want_type=*/false, + tf_none); + if (!field || TREE_CODE (field) != FIELD_DECL) + break; + } + else + { + gcc_assert (TREE_CODE (e->index) == FIELD_DECL); + field = e->index; + } + + if (desig_undo.start) + e->index = field; + } + if (desig_undo.start) + desig_undo.end = e; + goto found; + } + } + /* Not found, e.g. FIELD is a member of a base class. */ if (complain & tf_error) error ("%qD is not a direct member of %qT", field, type); @@ -7927,6 +8016,7 @@ reshape_init_class (tree type, reshape_i gcc_assert (aafield); field = aafield; direct_desig = false; + subclass = true; } } @@ -7958,6 +8048,18 @@ reshape_init_class (tree type, reshape_i d->cur->value, complain); d->cur++; } + else if (subclass) + { + if (complain & tf_warning) + warning (OPT_Wmissing_braces, + "missing braces around initializer for %qT", + TREE_TYPE (field)); + field_init = reshape_init_class (TREE_TYPE (field), d, + /*first_initializer_p=*/NULL_TREE, + complain); + if (TREE_CODE (field_init) == CONSTRUCTOR) + CONSTRUCTOR_BRACES_ELIDED_P (field_init) = true; + } else field_init = reshape_init_r (TREE_TYPE (field), d, /*first_initializer_p=*/NULL_TREE, --- gcc/testsuite/g++.dg/cpp/embed-14.C.jj 2026-07-27 12:45:24.363871030 +0200 +++ gcc/testsuite/g++.dg/cpp/embed-14.C 2026-08-05 12:33:10.571623985 +0200 @@ -3,8 +3,8 @@ struct S { int a; long b; unsigned char c[63]; int d; }; S s = { -#embed __FILE__ limit (64) prefix (.a = 1, .b = ) suffix (, .d = 2) // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++20 } } -}; +#embed __FILE__ limit (64) prefix (.a = 1, .b = ) suffix (, .d = 2) // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target { c++20 && c++26_down } } } +}; // { dg-error "designated initializer clause should not be followed by non-designated" "" { target c++29 } .-1 } const unsigned char t[66] = { #embed __FILE__ limit (64) prefix ([0] = 1, [1] =) suffix (, [65] = 2) // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++20 } } }; --- gcc/testsuite/g++.dg/cpp2a/desig2.C.jj 2026-07-27 12:45:24.363871030 +0200 +++ gcc/testsuite/g++.dg/cpp2a/desig2.C 2026-08-05 12:33:10.571793821 +0200 @@ -5,8 +5,10 @@ struct S { int a, b, c; }; S a = { 1, 2, 3 }; S b = { .a = 1, .b = 2, .c = 3 }; -S c = { 1, .b = 2, .c = 3 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++20 } } -S d = { .a = 1, 2, 3 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++20 } } +S c = { 1, .b = 2, .c = 3 }; // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target { c++20 && c++26_down } } } + // { dg-error "last non-designated initializer clause does not appertain to a base class subobject" "" { target c++20 } .-1 } +S d = { .a = 1, 2, 3 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target { c++20 && c++26_down } } } + // { dg-error "designated initializer clause should not be followed by non-designated" "" { target c++29 } .-1 } S e = { .b = 1, .b = 2 }; // { dg-error "designator used multiple times in the same initializer list" } #if __cplusplus > 201103L --- gcc/testsuite/g++.dg/cpp2a/desig13.C.jj 2026-07-27 12:45:24.363871030 +0200 +++ gcc/testsuite/g++.dg/cpp2a/desig13.C 2026-08-05 12:33:10.571953478 +0200 @@ -11,6 +11,7 @@ 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++20 } .-1 } + // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target { c++20 && c++26_down } } .-1 } + // { dg-error "designated initializer clause should not be followed by non-designated" "" { target c++29 } .-2 } bar ({.b = 1, .a = 2}); // { dg-error "designator order for field 'T::a' does not match declaration order in 'T'" } } --- gcc/testsuite/g++.dg/cpp2a/desig20.C.jj 2026-07-27 12:45:24.363871030 +0200 +++ gcc/testsuite/g++.dg/cpp2a/desig20.C 2026-08-05 12:33:10.572080321 +0200 @@ -16,5 +16,5 @@ struct B : A { int main() { [[maybe_unused]] B b = - { .a = 10, .d = 42 }; // { dg-error "not a direct member" } + { .a = 10, .d = 42 }; // { dg-error "not a direct member" "" { target c++26_down } } } --- gcc/testsuite/g++.dg/ext/desig4.C.jj 2026-07-27 12:45:24.364871017 +0200 +++ gcc/testsuite/g++.dg/ext/desig4.C 2026-08-05 12:33:10.572222207 +0200 @@ -5,10 +5,11 @@ char g[] = { [7] = "abcd" }; // { d int a = { .foo = 6 }; // { dg-error "designator" } int b = { [0] = 1 }; // { dg-error "12:designator .0." } _Complex float c = { .foo = 0, 1 }; // { dg-error "designator" } - // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++2a } .-1 } + // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target { c++20 && c++26_down } } .-1 } + // { dg-error "designated initializer clause should not be followed by non-designated" "" { target c++29 } .-2 } _Complex float d = { [0] = 0, 1 }; // { dg-error "23:designator .0." } - // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++2a } .-1 } + // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++20 } .-1 } _Complex float e = { 0, .foo = 1 }; // { dg-error "designator" } - // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++2a } .-1 } + // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target { c++20 && c++26_down } } .-1 } _Complex float f = { 0, [0] = 1 }; // { dg-error "26:designator .0." } - // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++2a } .-1 } + // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++20 } .-1 } --- gcc/testsuite/g++.dg/parse/pr43765.C.jj 2026-07-27 12:45:24.364871017 +0200 +++ gcc/testsuite/g++.dg/parse/pr43765.C 2026-08-05 12:33:10.572357776 +0200 @@ -10,8 +10,9 @@ const char *temp[] = {"607", "612", 0}; SomeType vals[] = { - { 0, values : temp, }, // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++2a } } + { 0, values : temp, }, // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target { c++20 && c++26_down } } } 0 }; // (note the error below is on the wrong line) -// { dg-error "initialization of flexible array member in a nested context" "" { target *-*-* } .-2 } +// { dg-error "initialization of flexible array member in a nested context" "" { target c++17_down } .-2 } +// { dg-error "last non-designated initializer clause does not appertain to a base class subobject" "" { target c++20 } .-3 } --- gcc/testsuite/g++.dg/cpp29/feat-cxx29.C.jj 2026-07-27 12:45:24.363871030 +0200 +++ gcc/testsuite/g++.dg/cpp29/feat-cxx29.C 2026-08-05 12:33:10.572895114 +0200 @@ -469,8 +469,8 @@ #ifndef __cpp_designated_initializers # error "__cpp_designated_initializers" -#elif __cpp_designated_initializers != 201707 -# error "__cpp_designated_initializers != 201707" +#elif __cpp_designated_initializers != 202606 +# error "__cpp_designated_initializers != 202606" #endif #ifndef __cpp_constexpr_in_decltype --- gcc/testsuite/g++.dg/cpp29/desig1.C.jj 2026-08-05 12:33:10.573049148 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig1.C 2026-08-05 12:33:10.573049148 +0200 @@ -0,0 +1,12 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++20 } } + +struct A { int a; }; +struct B : A { int b; }; +auto b1 = B { { 1 }, 2 }; +auto b2 = B { 1, 2 }; +auto b3 = B { .a = 1, .b = 2 }; // { dg-error "'B' has no non-static data member named 'a'" "" { target c++26_down } } +auto b4 = B { { .a = 1 }, .b = 2 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto b5 = B { .a { 1 }, .b { 2 } }; // { dg-error "'B' has no non-static data member named 'a'" "" { target c++26_down } } +auto b6 = B { .b = 2, .a = 1 }; // { dg-error "designator order for field 'B::A' does not match declaration order in 'B'" "" { target c++29 } } + // { dg-error "'B' has no non-static data member named 'a'" "" { target c++26_down } .-1 } --- gcc/testsuite/g++.dg/cpp29/desig2.C.jj 2026-08-05 12:33:10.573137909 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig2.C 2026-08-05 12:33:10.573137909 +0200 @@ -0,0 +1,9 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++20 } } + +struct A { A (const char *); int a; }; +struct B : A { int b, c; }; +auto b1 = B { "hello", .b = 3, .c = 4 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto b2 = B { { "hello" }, .b = 3, .c = 4 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto b3 = B { "nope", 3, .c = 4 }; // { dg-error "last non-designated initializer clause does not appertain to a base class subobject" } + // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } .-1 } --- gcc/testsuite/g++.dg/cpp29/desig3.C.jj 2026-08-05 12:33:10.573225565 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig3.C 2026-08-05 12:33:10.573225565 +0200 @@ -0,0 +1,41 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++29 } } + +struct A { int a; }; +struct B : A { int b; }; +constexpr auto b1 = B { { 1 }, 2 }; +constexpr auto b2 = B { 3, 4 }; +constexpr auto b3 = B { .a = 5, .b = 6 }; +constexpr auto b4 = B { { .a = 7 }, .b = 8 }; +constexpr auto b5 = B { .a { 9 }, .b { 10 } }; +static_assert (b1.a == 1 && b1.b == 2); +static_assert (b2.a == 3 && b2.b == 4); +static_assert (b3.a == 5 && b3.b == 6); +static_assert (b4.a == 7 && b4.b == 8); +static_assert (b5.a == 9 && b5.b == 10); + +struct C { constexpr C (const char *x) : a (0) { while (*x) a += *x++; } int a; }; +struct D : C { int b, c; }; +constexpr auto d1 = D { "abc", .b = 3, .c = 4 }; +constexpr auto d2 = D { { "de" }, .b = 5, .c = 6 }; +static_assert (d1.a == 'a' + 'b' + 'c' && d1.b == 3 && d1.c == 4); +static_assert (d2.a == 'd' + 'e' && d2.b == 5 && d2.c == 6); + +struct E { int x; }; +struct F : E { int x; }; +constexpr auto f1 = F { .x = 1 }; +constexpr auto f2 = F { { .x = 2 }, .x = 3 }; +constexpr auto f3 = F { E { 4 }, .x = 5 }; +static_assert (f1.E::x == 0 && f1.F::x == 1); +static_assert (f2.E::x == 2 && f2.F::x == 3); +static_assert (f3.E::x == 4 && f3.F::x == 5); + +struct G { int g; }; +struct H { int h; }; +struct I : G, H { int i; }; +constexpr auto i1 = I { { .g = 1 }, { .h = 2 }, .i = 3 }; +constexpr auto i2 = I { { .g = 4 }, .h = 5, .i = 6 }; +constexpr auto i3 = I { { .g = 7 }, H { 8 }, .i = 9 }; +static_assert (i1.g == 1 && i1.h == 2 && i1.i == 3); +static_assert (i2.g == 4 && i2.h == 5 && i2.i == 6); +static_assert (i3.g == 7 && i3.h == 8 && i3.i == 9); --- gcc/testsuite/g++.dg/cpp29/desig4.C.jj 2026-08-05 12:33:10.573338790 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig4.C 2026-08-05 12:33:10.573338790 +0200 @@ -0,0 +1,13 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++20 } } + +struct F { int f; }; +struct G { int g; }; +struct H : F, G { int h; }; +auto h1 = H { { .f = 1 }, { .g = 2 }, .h = 3 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto h2 = H { { .f = 1 }, .g = 2, .h = 3 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } + // { dg-error "'H' has no non-static data member named 'g'" "" { target c++26_down } .-1 } +auto h3 = H { { .f = 1 }, G { 2 }, .h = 3 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto h4 = H { { .f = 1 }, { .g = 2 }, .g = 3, .h = 4 }; // { dg-error "designator order for field 'H::G' does not match declaration order in 'H'" "" { target c++29 } } + // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } .-1 } + // { dg-error "'H' has no non-static data member named 'g'" "" { target c++26_down } .-2 } --- gcc/testsuite/g++.dg/cpp29/desig5.C.jj 2026-08-05 12:33:10.573429982 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig5.C 2026-08-05 12:33:10.573429982 +0200 @@ -0,0 +1,25 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++20 } } + +struct A { int a; }; +struct B : A { int b; }; +struct C : A { C (); int c; }; +struct D : C { int d; }; + +auto a = A { .a = 1 }; +auto b = B { .a = 1, .b = 2 }; // { dg-error "'B' has no non-static data member named 'a'" "" { target c++26_down } } +auto c = C { .c = 1 }; // { dg-error "designated initializers cannot be used with a non-aggregate type 'C'" } + // { dg-error "no matching function for call to" "" { target *-*-* } .-1 } +auto d = D { .a = 1 }; // { dg-error "designated initializers cannot be used with a non-aggregate type 'C'" "" { target c++29 } } + // { dg-error "'D' has no non-static data member named 'a'" "" { target c++26_down } .-1 } + +struct E { int x; }; +struct F : E { int x; }; +constexpr auto f = F { .x = 1 }; +static_assert (f.E::x == 0 && f.F::x == 1); + +struct G { int x; }; +struct H { int x; }; +struct I : G, H { }; +auto i = I { .x = 1 }; // { dg-error "request for member 'x' is ambiguous" "" { target c++29 } } + // { dg-error "'I' has no non-static data member named 'x'" "" { target c++26_down } .-1 } --- gcc/testsuite/g++.dg/cpp29/desig6.C.jj 2026-08-05 12:33:10.573513578 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig6.C 2026-08-05 12:33:10.573513578 +0200 @@ -0,0 +1,16 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++29 } } + +struct A { int a1, a2; }; +struct B : A { int b; }; +struct C : A { int a1; }; +A v0 = A { 1, .a2 = 2 }; // { dg-error "last non-designated initializer clause does not appertain to a base class subobject" } +constexpr B v1 = B { .a1 = 1, .b = 2 }; // the explicitly initialized elements are [A, B::b] +static_assert (v1.a1 == 1 && v1.a2 == 0 && v1.b == 2); +constexpr B v2 = B { .a1 = 1, .a2 = 2, .b = 3 };// the explicitly initialized elements are [A, B::b] +static_assert (v2.a1 == 1 && v2.a2 == 2 && v2.b == 3); +constexpr B v3 = B { A { 1, 2 }, .b = 3 }; // the explicitly initialized elements are [A, B::b] +static_assert (v3.a1 == 1 && v3.a2 == 2 && v3.b == 3); +B v4 = B { A { }, .a2 = 1, .b = 3 }; // { dg-error "designator order for field 'B::A' does not match declaration order in 'B'" } +constexpr C v5 = C { .a1 = 4 }; // the explicitly initialized elements are [C::a1] +static_assert (v5.A::a1 == 0 && v5.a2 == 0 && v5.C::a1 == 4); --- gcc/testsuite/g++.dg/cpp29/desig7.C.jj 2026-08-05 12:33:10.573602181 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig7.C 2026-08-05 12:33:10.573602181 +0200 @@ -0,0 +1,10 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++29 } } + +struct A { int a; }; +struct B : A { int b; }; +struct C : B { int c; }; +constexpr B x = B { .a = 1 }; +static_assert (x.a == 1 && x.b == 0); +constexpr C y = C { .a = 2, .b = 3, .c = 4 }; +static_assert (y.a == 2 && y.b == 3 && y.c == 4); --- gcc/testsuite/g++.dg/cpp29/desig8.C.jj 2026-08-05 12:33:10.573681626 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig8.C 2026-08-05 12:33:10.573681626 +0200 @@ -0,0 +1,19 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++29 } } + +struct A { int x; int y; int z; }; +A a { .y = 2, .x = 1 }; // { dg-error "designator order for field 'A::x' does not match declaration order in 'A'" } +constexpr A b { .x = 1, .z = 2 }; +static_assert (b.x == 1 && b.y == 0 && b.z == 2); +struct B : A { int q; }; +constexpr B e { .x = 1, .q = 3 }; +static_assert (e.x == 1 && e.y == 0 && e.z == 0 && e.q == 3); +B f { .q = 3, .x = 1 }; // { dg-error "designator order for field 'B::A' does not match declaration order in 'B'" } +struct C { int p; int x; }; +struct D : A, C { }; +constexpr D g { .y = 1, .p = 2 }; +static_assert (g.A::x == 0 && g.y == 1 && g.z == 0 && g.p == 2 && g.C::x == 0); +D h { .x = 2 }; // { dg-error "request for member 'x' is ambiguous" } +struct NonAggr { int na; NonAggr (int); }; +struct E : NonAggr { int e; }; +E i { .na = 1, .e = 2 }; // { dg-error "designated initializers cannot be used with a non-aggregate type 'NonAggr'" } --- gcc/testsuite/g++.dg/cpp29/desig9.C.jj 2026-08-05 12:33:10.573761610 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig9.C 2026-08-05 12:33:10.573761610 +0200 @@ -0,0 +1,12 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++20 } } + +struct A { int a; }; +struct B : A { int b; }; +void foo (A); // { dg-message "candidate 1: 'void foo\\\(A\\\)'" "" { target c++29 } } +void foo (B); // { dg-message "candidate 2: 'void foo\\\(B\\\)'" "" { target c++29 } } +void +bar () +{ + foo ({ .a = 1 }); // { dg-error "call of overloaded 'foo\\\(<brace-enclosed initializer list>\\\)' is ambiguous" "" { target c++29 } } +} // { dg-message "there are 2 candidates" "" { target c++29 } .-1 } --- gcc/testsuite/g++.dg/cpp29/desig10.C.jj 2026-08-05 12:33:10.573846748 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig10.C 2026-08-05 12:33:10.573846748 +0200 @@ -0,0 +1,43 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++29 } } + +struct A { int a, b, c; }; +struct B { int d, e; }; +struct C : A { int f, g; }; +struct D : B { int h, i; }; +struct E : C, D { int j; }; +constexpr auto e1 = E { .a = 1, .c = 2, .f = 3, .d = 4, .i = 5, .j = 6 }; +static_assert (e1.a == 1 && e1.b == 0 && e1.c == 2 && e1.f == 3 && e1.g == 0 + && e1.d == 4 && e1.e == 0 && e1.h == 0 && e1.i == 5 && e1.j == 6); +auto e2 = E { 1, 2, 3, 4, 5, .j = 6 }; // { dg-error "last non-designated initializer clause does not appertain to a base class subobject" } +constexpr auto e3 = E { { .a = 1, .b = 2 }, .j = 3 }; +static_assert (e3.a == 1 && e3.b == 2 && e3.c == 0 && e3.f == 0 && e3.g == 0 + && e3.d == 0 && e3.e == 0 && e3.h == 0 && e3.i == 0 && e3.j == 3); +constexpr auto e4 = E { {}, { { .e = 1 }, .i = 2 }, .j = 3 }; +static_assert (e4.a == 0 && e4.b == 0 && e4.c == 0 && e4.f == 0 && e4.g == 0 + && e4.d == 0 && e4.e == 1 && e4.h == 0 && e4.i == 2 && e4.j == 3); +constexpr auto e5 = E { { { .b = 1 }, .f = 2 }, { { .d = 3 }, .h = 4 }, .j = 5 }; +static_assert (e5.a == 0 && e5.b == 1 && e5.c == 0 && e5.f == 2 && e5.g == 0 + && e5.d == 3 && e5.e == 0 && e5.h == 4 && e5.i == 0 && e5.j == 5); +constexpr auto e6 = E { .a = 1, .b = 2, .c = 3, .f = 4, .g = 5, .d = 6, + .e = 7, .h = 8, .i = 9, .j = 10 }; +static_assert (e6.a == 1 && e6.b == 2 && e6.c == 3 && e6.f == 4 && e6.g == 5 + && e6.d == 6 && e6.e == 7 && e6.h == 8 && e6.i == 9 && e6.j == 10); +auto e7 = E { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, .g = 7, + .h = 8, .i = 9, .j = 10 };// { dg-error "designator order for field 'E::C' does not match declaration order in 'E'" } +auto e8 = E { {}, {}, .a = 1, .j = 2 }; // { dg-error "designator order for field 'E::C' does not match declaration order in 'E'" } +auto e9 = E { {}, .f = 1 }; // { dg-error "designator order for field 'E::C' does not match declaration order in 'E'" } +auto e10 = E { {}, {}, .e = 1 }; // { dg-error "designator order for field 'E::D' does not match declaration order in 'E'" } +auto e11 = E { {}, {}, .h = 1 }; // { dg-error "designator order for field 'E::D' does not match declaration order in 'E'" } +auto e12 = E { .a = 0, .b = 0, .c = 0, .d = 0, .e = 0, .f = 0, .g = 0, .h = 0, .i = 0, .j = 0, + .a = 0, .b = 0, .c = 0, .d = 0, .e = 0, .f = 0, .g = 0, .h = 0, .i = 0, .j = 0 }; +// { dg-error "'.a' designator used multiple times in the same initializer list" "" { target *-*-* } .-1 } +// { dg-error "'.b' designator used multiple times in the same initializer list" "" { target *-*-* } .-2 } +// { dg-error "'.c' designator used multiple times in the same initializer list" "" { target *-*-* } .-3 } +// { dg-error "'.d' designator used multiple times in the same initializer list" "" { target *-*-* } .-4 } +// { dg-error "'.e' designator used multiple times in the same initializer list" "" { target *-*-* } .-5 } +// { dg-error "'.f' designator used multiple times in the same initializer list" "" { target *-*-* } .-6 } +// { dg-error "'.g' designator used multiple times in the same initializer list" "" { target *-*-* } .-7 } +// { dg-error "'.h' designator used multiple times in the same initializer list" "" { target *-*-* } .-8 } +// { dg-error "'.i' designator used multiple times in the same initializer list" "" { target *-*-* } .-9 } +// { dg-error "'.j' designator used multiple times in the same initializer list" "" { target *-*-* } .-10 } --- gcc/testsuite/g++.dg/cpp29/desig11.C.jj 2026-08-05 12:33:10.573940360 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig11.C 2026-08-05 12:33:10.573940360 +0200 @@ -0,0 +1,21 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++29 } } + +struct A { int a, x; }; +struct B { int b, x; }; +struct C : A, B { int c; }; +struct D : C { int x; }; +constexpr auto d = D { .c = 1, .x = 2 }; +static_assert (d.a == 0 && d.A::x == 0 && d.b == 0 && d.B::x == 0 + && d.c == 1 && d.D::x == 2); +struct E { int a, x; }; +struct F : E { int x; }; +constexpr auto f = F { .a = 1, .x = 2 }; +static_assert (f.a == 1 && f.E::x == 0 && f.F::x == 2); +struct G { int a, b; }; +struct H : G { int c; }; +struct I { int a, b, d; }; +struct J : I { int e, c; }; +constexpr int foo (H, G) { return 1; } +constexpr int foo (J, I) { return 42; } +static_assert (foo ({ .a = 1, .b = 2, .c = 3 }, { .d = 1 }) == 42); --- gcc/testsuite/g++.dg/cpp29/desig12.C.jj 2026-08-05 12:33:10.574033956 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig12.C 2026-08-05 12:33:10.574033956 +0200 @@ -0,0 +1,13 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++20 } } +// { dg-options "-Wno-c++29-extensions" } + +struct A { int a, b; }; +struct B { int c, d; }; +struct C : A, B { int e, f; }; +auto c1 = C { { .a = 1, .b = 2 }, { .c = 3, .d = 4 }, .e = 5, .f = 6 }; +auto c2 = C { { .a = 1, .b = 2 }, .e = 5, .f = 6 }; +auto c3 = C { {}, { .c = 3, .d = 4 }, .f = 6 }; +auto c4 = C { .e = 1, 2 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } + // { dg-error "designated initializer clause should not be followed by non-designated" "" { target c++29 } .-1 } +auto a1 = A { 1, .b = 2 }; // { dg-error "last non-designated initializer clause does not appertain to a base class subobject" } --- gcc/testsuite/g++.dg/cpp29/desig13.C.jj 2026-08-05 12:33:10.574115783 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig13.C 2026-08-05 12:33:10.574115783 +0200 @@ -0,0 +1,14 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++20 } } +// { dg-options "-Wc++29-extensions" } + +struct A { int a, b; }; +struct B { int c, d; }; +struct C : A, B { int e, f; }; +auto c1 = C { { .a = 1, .b = 2 }, { .c = 3, .d = 4 }, .e = 5, .f = 6 }; // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto c2 = C { { .a = 1, .b = 2 }, .e = 5, .f = 6 }; // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto c3 = C { {}, { .c = 3, .d = 4 }, .f = 6 }; // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto c4 = C { .e = 1, 2 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } + // { dg-error "designated initializer clause should not be followed by non-designated" "" { target c++29 } .-1 } +auto a1 = A { 1, .b = 2 }; // { dg-error "last non-designated initializer clause does not appertain to a base class subobject" } + // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } .-1 } --- gcc/testsuite/g++.dg/cpp29/desig14.C.jj 2026-08-05 12:33:10.574200452 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig14.C 2026-08-05 12:57:26.171939454 +0200 @@ -0,0 +1,23 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++20 } } +// { dg-options "-Wmissing-braces" } + +struct A { int a, b; }; +struct B { int c, d; }; +struct C : A, B { int e, f; }; +struct D : C { int g; }; +auto c1 = C { { .a = 1, .b = 2 }, { .c = 3, .d = 4 }, .e = 5, .f = 6 }; // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto c2 = C { { .a = 1, .b = 2 }, .e = 5, .f = 6 }; // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto c3 = C { {}, { .c = 3, .d = 4 }, .f = 6 }; // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto c4 = C { .e = 1, 2 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } + // { dg-error "designated initializer clause should not be followed by non-designated" "" { target c++29 } .-1 } +auto c5 = C { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6 }; // { dg-error "'C' has no non-static data member named 'a'" "" { target c++26_down } } + // { dg-warning "missing braces around initializer for 'A'" "" { target c++29 } .-1 } + // { dg-warning "missing braces around initializer for 'B'" "" { target c++29 } .-2 } +auto a1 = A { 1, .b = 2 }; // { dg-error "last non-designated initializer clause does not appertain to a base class subobject" } + // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } .-1 } +auto d1 = D { .a = 1, .b = 2, .c = 3, .d = 4, .e = 5, .f = 6, .g = 7 }; // { dg-error "'D' has no non-static data member named 'a'" "" { target c++26_down } } + // { dg-warning "missing braces around initializer for 'A'" "" { target c++29 } .-1 } + // { dg-warning "missing braces around initializer for 'B'" "" { target c++29 } .-2 } + // { dg-warning "missing braces around initializer for 'C'" "" { target c++29 } .-3 } +auto d2 = D { { { .a = 1, .b = 2 }, { .c = 3, .d = 4 }, .e = 5, .f = 6 }, .g = 7 }; // { dg-warning "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } --- gcc/testsuite/g++.dg/cpp29/desig15.C.jj 2026-08-05 12:33:10.574280785 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig15.C 2026-08-05 12:33:10.574280785 +0200 @@ -0,0 +1,15 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++20 } } +// { dg-options "-Werror=c++29-extensions" } + +struct A { int a, b; }; +struct B { int c, d; }; +struct C : A, B { int e, f; }; +auto c1 = C { { .a = 1, .b = 2 }, { .c = 3, .d = 4 }, .e = 5, .f = 6 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto c2 = C { { .a = 1, .b = 2 }, .e = 5, .f = 6 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto c3 = C { {}, { .c = 3, .d = 4 }, .f = 6 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto c4 = C { .e = 1, 2 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } + // { dg-error "designated initializer clause should not be followed by non-designated" "" { target c++29 } .-1 } +auto a1 = A { 1, .b = 2 }; // { dg-error "last non-designated initializer clause does not appertain to a base class subobject" } + // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } .-1 } + // { dg-message "some warnings being treated as errors" "" { target c++26_down } 0 } --- gcc/testsuite/g++.dg/cpp29/desig16.C.jj 2026-08-05 12:33:10.574369740 +0200 +++ gcc/testsuite/g++.dg/cpp29/desig16.C 2026-08-05 12:33:10.574369740 +0200 @@ -0,0 +1,13 @@ +// C++29 P2287R6 - Designated-initializers for Base Classes +// { dg-do compile { target c++20 } } + +struct A { int a, b; }; +struct B { int c, d; }; +struct C : A, B { int e, f; }; +auto c1 = C { { .a = 1, .b = 2 }, { .c = 3, .d = 4 }, .e = 5, .f = 6 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto c2 = C { { .a = 1, .b = 2 }, .e = 5, .f = 6 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto c3 = C { {}, { .c = 3, .d = 4 }, .f = 6 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } +auto c4 = C { .e = 1, 2 }; // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } } + // { dg-error "designated initializer clause should not be followed by non-designated" "" { target c++29 } .-1 } +auto a1 = A { 1, .b = 2 }; // { dg-error "last non-designated initializer clause does not appertain to a base class subobject" } + // { dg-error "either all initializer clauses should be designated or none of them should be" "" { target c++26_down } .-1 }