diff mbox series

c++: Add [dcl.init.aggr] examples to testsuite

Message ID ZpkPaiT0/JrwtKs0@tucnak
State New
Headers show
Series c++: Add [dcl.init.aggr] examples to testsuite | expand

Commit Message

Jakub Jelinek July 18, 2024, 12:49 p.m. UTC
Hi!

When working on the #embed optimization support, I went recently through
all of reshape_init_r* and today I read in detail all the P3106R1 changes
and I believe we implement it that way for years.
To double check that, I've added tests with the current [dcl.init.aggr]
examples but tested in all the languages from C++98 to C++26, of course
guarded as needed for constructs which require newer versions of C++.
The examples come in two tests, one is a runtime test for the non-erroneous
examples, the other is a compile time test for the diagnostics.
The former one includes mostly intact examples with runtime checking (both
to test what is written in the section exactly and to test at least
something with C++98) and then when useful also adds constexpr tests with
static_asserts for C++11 and later.

Tested on x86_64-linux and i686-linux with
GXX_TESTSUITE_STDS=98,11,14,17,20,23,26 make check-g++ RUNTESTFLAGS='dg.exp=aggr-init*.C'
Ok for trunk?

Also tested on GCC 11 branch with
GXX_TESTSUITE_STDS=98,11,14,17,20,2b make check-g++ RUNTESTFLAGS='dg.exp=aggr-init*.C'
where just the " is a GCC extension" part of one error is left out,
otherwise it passes the same, ditto with clang 14 (of course with different
diagnostics, but verified it emits diagnostics on the right lines), so I
believe we can claim implementation of this DR paper, either in all versions
or at least in GCC 11+.

2024-07-18  Jakub Jelinek  <jakub@redhat.com>

	PR c++/114460
	* g++.dg/cpp26/aggr-init1.C: New test.
	* g++.dg/cpp26/aggr-init2.C: New test.


	Jakub

Comments

Jason Merrill July 19, 2024, 2:34 a.m. UTC | #1
On 7/18/24 8:49 AM, Jakub Jelinek wrote:
> Hi!
> 
> When working on the #embed optimization support, I went recently through
> all of reshape_init_r* and today I read in detail all the P3106R1 changes
> and I believe we implement it that way for years.
> To double check that, I've added tests with the current [dcl.init.aggr]
> examples but tested in all the languages from C++98 to C++26, of course
> guarded as needed for constructs which require newer versions of C++.
> The examples come in two tests, one is a runtime test for the non-erroneous
> examples, the other is a compile time test for the diagnostics.
> The former one includes mostly intact examples with runtime checking (both
> to test what is written in the section exactly and to test at least
> something with C++98) and then when useful also adds constexpr tests with
> static_asserts for C++11 and later.
> 
> Tested on x86_64-linux and i686-linux with
> GXX_TESTSUITE_STDS=98,11,14,17,20,23,26 make check-g++ RUNTESTFLAGS='dg.exp=aggr-init*.C'
> Ok for trunk?
> 
> Also tested on GCC 11 branch with
> GXX_TESTSUITE_STDS=98,11,14,17,20,2b make check-g++ RUNTESTFLAGS='dg.exp=aggr-init*.C'
> where just the " is a GCC extension" part of one error is left out,
> otherwise it passes the same, ditto with clang 14 (of course with different
> diagnostics, but verified it emits diagnostics on the right lines), so I
> believe we can claim implementation of this DR paper, either in all versions
> or at least in GCC 11+.

OK, thanks.  And yes, the intention of P3106 was to better describe the 
behavior of existing implementations.

> 2024-07-18  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/114460
> 	* g++.dg/cpp26/aggr-init1.C: New test.
> 	* g++.dg/cpp26/aggr-init2.C: New test.
> 
> --- gcc/testsuite/g++.dg/cpp26/aggr-init1.C.jj	2024-07-18 12:25:03.767375097 +0200
> +++ gcc/testsuite/g++.dg/cpp26/aggr-init1.C	2024-07-18 14:27:48.324677997 +0200
> @@ -0,0 +1,341 @@
> +// P3106R1 - Clarifying rules for brace elision in aggregate initialization
> +// Examples from C++26 [dcl.init.aggr]
> +// { dg-do run }
> +
> +extern "C" void abort ();
> +
> +namespace N1 {
> +#if __cpp_designated_initializers >= 201707L
> +  struct C {
> +    union {
> +      int a;
> +      const char* p;
> +    };
> +    int x;
> +  } c = { .a = 1, .x = 3 };
> +  constexpr C c2 = { .a = 1, .x = 3 };
> +  static_assert (c2.a == 1 && c2.x == 3, "");
> +#endif
> +
> +  bool
> +  test ()
> +  {
> +#if __cpp_designated_initializers >= 201707L
> +    return c.a == 1 && c.x == 3;
> +#else
> +    return true;
> +#endif
> +  }
> +}
> +
> +namespace N2 {
> +  struct A {
> +    int x;
> +    struct B {
> +      int i;
> +      int j;
> +    } b;
> +  } a = { 1, { 2, 3 } };
> +
> +#if __cplusplus >= 201703L
> +  struct base1 { int b1, b2 = 42; };
> +  struct base2 {
> +    base2 () { b3 = 42; }
> +    int b3;
> +  };
> +  struct derived : base1, base2 {
> +    int d;
> +  };
> +
> +  derived d1 { { 1, 2 }, {}, 4 };
> +  derived d2 { {}, {}, 4 };
> +#endif
> +
> +  bool
> +  test ()
> +  {
> +    return a.x == 1 && a.b.i == 2 && a.b.j == 3
> +#if __cplusplus >= 201703L
> +	   && d1.b1 == 1 && d1.b2 == 2 && d1.b3 == 42 && d1.d == 4
> +	   && d2.b1 == 0 && d2.b2 == 42 && d2.b3 == 42 && d2.d == 4
> +#endif
> +	   ;
> +  }
> +
> +#if __cplusplus >= 201703L
> +  constexpr A a2 = { 1, { 2, 3 } };
> +  static_assert (a2.x == 1 && a2.b.i == 2 && a2.b.j == 3, "");
> +
> +  struct base3 {
> +#if __cplusplus >= 202002L
> +    constexpr base3 () { b3 = 42; }
> +#else
> +    constexpr base3 () : b3 (42) {}
> +#endif
> +    int b3;
> +  };
> +  struct derived2 : base1, base3 {
> +    int d;
> +  };
> +  constexpr derived2 d3 { { 1, 2}, {}, 4};
> +  constexpr derived2 d4 { {}, {}, 4 };
> +  static_assert (d3.b1 == 1 && d3.b2 == 2 && d3.b3 == 42 && d3.d == 4, "");
> +  static_assert (d4.b1 == 0 && d4.b2 == 42 && d4.b3 == 42 && d4.d == 4, "");
> +#endif
> +}
> +
> +namespace N3 {
> +#if __cplusplus >= 201402L
> +  struct S { int a; const char *b; int c; int d = b[a]; };
> +  S ss = { 1, "asdf" };
> +  constexpr S ss2 = { 1, "asdf" };
> +  static_assert (ss2.a == 1 && ss2.b[0] == 'a' && ss2.b[3] == 'f' && ss2.c == 0 && ss2.d == 's', "");
> +
> +#if __cpp_designated_initializers >= 201707L
> +  struct string { int s = -42; };
> +  struct A {
> +    string a;
> +    int b = 42;
> +    int c = -1;
> +  };
> +  static_assert (A { .c = 21 }.a.s == -42 && A { .c = 21 }.b == 42 && A { .c = 21 }.c == 21, "");
> +#endif
> +#endif
> +
> +  bool
> +  test ()
> +  {
> +#if __cplusplus >= 201402L
> +    return ss.a == 1 && __builtin_strcmp (ss.b, "asdf") == 0 && ss.c == 0 && ss.d == 's';
> +#else
> +    return true;
> +#endif
> +  }
> +}
> +
> +namespace N4 {
> +  int x[] = { 1, 3, 5 };
> +
> +  bool
> +  test ()
> +  {
> +    return sizeof (x) == 3 * sizeof (int) && x[0] == 1 && x[1] == 3 && x[2] == 5;
> +  }
> +
> +#if __cplusplus >= 201103L
> +  constexpr int x2[] = { 1, 3, 5 };
> +  static_assert (sizeof (x2) == 3 * sizeof (int)
> +		 && x2[0] == 1 && x2[1] == 3 && x2[2] == 5, "");
> +#endif
> +}
> +
> +namespace N5 {
> +  struct X { int i, j, k; };
> +  X a[] = { 1, 2, 3, 4, 5, 6 };
> +  X b[2] = { { 1, 2, 3 }, { 4, 5, 6 } };
> +
> +  bool
> +  test ()
> +  {
> +    return sizeof (a) == sizeof (b) && __builtin_memcmp (a, b, sizeof (a)) == 0;
> +  }
> +
> +#if __cplusplus >= 201103L
> +  constexpr X a2[] = { 1, 2, 3, 4, 5, 6 };
> +  constexpr X b2[2] = { { 1, 2, 3 }, { 4, 5, 6 } };
> +  static_assert (sizeof (a2) == 2 * sizeof (X)
> +		 && a2[0].i == 1 && a2[0].j == 2 && a2[0].k == 3
> +		 && a2[1].i == 4 && a2[1].j == 5 && a2[1].k == 6, "");
> +  static_assert (sizeof (b2) == 2 * sizeof (X)
> +		 && b2[0].i == 1 && b2[0].j == 2 && b2[0].k == 3
> +		 && b2[1].i == 4 && b2[1].j == 5 && b2[1].k == 6, "");
> +#endif
> +}
> +
> +namespace N7 {
> +  struct A {
> +    int i;
> +    static int s;
> +    int j;
> +    int : 17;
> +    int k;
> +  } a = { 1, 2, 3 };
> +
> +  bool
> +  test ()
> +  {
> +    return a.i == 1 && a.j == 2 && a.k == 3;
> +  }
> +
> +#if __cplusplus >= 201103L
> +  constexpr A a2 = { 1, 2, 3 };
> +  static_assert (a2.i == 1 && a2.j == 2 && a2.k == 3, "");
> +#endif
> +}
> +
> +namespace N9 {
> +  int x[2][2] = { 3, 1, 4, 2 };
> +  float y[4][3] = {
> +    { 1 }, { 2 }, { 3 }, { 4 }
> +  };
> +
> +  bool
> +  test ()
> +  {
> +    return x[0][0] == 3 && x[0][1] == 1 && x[1][0] == 4 && x[1][1] == 2
> +	   && y[0][0] == 1.f && y[0][1] == 0.f && y[0][2] == 0.f
> +	   && y[1][0] == 2.f && y[1][1] == 0.f && y[1][2] == 0.f
> +	   && y[2][0] == 3.f && y[2][1] == 0.f && y[2][2] == 0.f
> +           && y[3][0] == 4.f && y[3][1] == 0.f && y[3][2] == 0.f;
> +  }
> +
> +#if __cplusplus >= 201103L
> +  constexpr int x2[2][2] = { 3, 1, 4, 2 };
> +  constexpr float y2[4][3] = {
> +    { 1 }, { 2 }, { 3 }, { 4 }
> +  };
> +  static_assert (x2[0][0] == 3 && x2[0][1] == 1 && x2[1][0] == 4 && x2[1][1] == 2, "");
> +  static_assert (y2[0][0] == 1.f && y2[0][1] == 0.f && y2[0][2] == 0.f, "");
> +  static_assert (y2[1][0] == 2.f && y2[1][1] == 0.f && y2[1][2] == 0.f, "");
> +  static_assert (y2[2][0] == 3.f && y2[2][1] == 0.f && y2[2][2] == 0.f, "");
> +  static_assert (y2[3][0] == 4.f && y2[3][1] == 0.f && y2[3][2] == 0.f, "");
> +#endif
> +}
> +
> +namespace N10 {
> +  struct S1 { int a, b; };
> +  struct S2 { S1 s, t; };
> +
> +  S2 x[2] = { 1, 2, 3, 4, 5, 6, 7, 8 };
> +  S2 y[2] = {
> +    {
> +      { 1, 2 },
> +      { 3, 4 }
> +    },
> +    {
> +      { 5, 6 },
> +      { 7, 8 }
> +    }
> +  };
> +
> +  bool
> +  test ()
> +  {
> +    return sizeof (x) == sizeof (y) && __builtin_memcmp (x, y, sizeof (x)) == 0;
> +  }
> +
> +#if __cplusplus >= 201103L
> +  constexpr S2 x2[2] = { 1, 2, 3, 4, 5, 6, 7, 8 };
> +  constexpr S2 y2[2] = { { { 1, 2 }, { 3, 4 } }, { { 5, 6 }, { 7, 8 } } };
> +  static_assert (x2[0].s.a == 1 && x2[0].s.b == 2 && x2[0].t.a == 3 && x2[0].t.b == 4, "");
> +  static_assert (x2[1].s.a == 5 && x2[1].s.b == 6 && x2[1].t.a == 7 && x2[1].t.b == 8, "");
> +  static_assert (y2[0].s.a == 1 && y2[0].s.b == 2 && y2[0].t.a == 3 && y2[0].t.b == 4, "");
> +  static_assert (y2[1].s.a == 5 && y2[1].s.b == 6 && y2[1].t.a == 7 && y2[1].t.b == 8, "");
> +#endif
> +}
> +
> +namespace N12 {
> +  float y[4][3] = {
> +    { 1, 3, 5 },
> +    { 2, 4, 6 },
> +    { 3, 5, 7 },
> +  };
> +  float y2[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 };
> +
> +  bool
> +  test ()
> +  {
> +    for (int i = 0; i < 4; ++i)
> +      for (int j = 0; j < 3; ++j)
> +	if (y[i][j] != (i == 3 ? 0.f : (float) (i + 1 + j * 2))
> +	    || y[i][j] != y2[i][j])
> +	  return false;
> +    return true;
> +  }
> +
> +#if __cplusplus >= 201103L
> +  constexpr float y3[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, };
> +  constexpr float y4[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 };
> +  static_assert (y3[0][0] == 1.f && y3[0][1] == 3.f && y3[0][2] == 5.f, "");
> +  static_assert (y3[1][0] == 2.f && y3[1][1] == 4.f && y3[1][2] == 6.f, "");
> +  static_assert (y3[2][0] == 3.f && y3[2][1] == 5.f && y3[2][2] == 7.f, "");
> +  static_assert (y3[3][0] == 0.f && y3[3][1] == 0.f && y3[3][2] == 0.f, "");
> +  static_assert (y4[0][0] == 1.f && y4[0][1] == 3.f && y4[0][2] == 5.f, "");
> +  static_assert (y4[1][0] == 2.f && y4[1][1] == 4.f && y4[1][2] == 6.f, "");
> +  static_assert (y4[2][0] == 3.f && y4[2][1] == 5.f && y4[2][2] == 7.f, "");
> +  static_assert (y4[3][0] == 0.f && y4[3][1] == 0.f && y4[3][2] == 0.f, "");
> +#endif
> +}
> +
> +namespace N13 {
> +  bool
> +  test ()
> +  {
> +    struct S { } s;
> +    struct A {
> +      S s1;
> +      int i1;
> +      S s2;
> +      int i2;
> +      S s3;
> +      int i3;
> +    } a = {
> +      { },
> +      0,
> +      s,
> +      0
> +    };
> +    return a.i1 == 0 && a.i2 == 0;
> +  }
> +}
> +
> +namespace N14 {
> +  struct A {
> +    int i;
> +    operator int ();
> +  };
> +  struct B {
> +    A a1, a2;
> +    int z;
> +  };
> +  A a;
> +  B b = { 4, a, a };
> +  A::operator int () { return 42; }
> +
> +  bool
> +  test ()
> +  {
> +    return b.a1.i == 4 && b.a2.i == 0 && b.z == 42;
> +  }
> +
> +#if __cplusplus >= 201103L
> +  struct A2 {
> +    int i;
> +    constexpr operator int () const { return 42; }
> +  };
> +  struct B2 {
> +    A2 a1, a2;
> +    int z;
> +  };
> +  constexpr A2 a2 = { 26 };
> +  constexpr B2 b2 = { 4, a2, a2 };
> +  static_assert (b2.a1.i == 4 && b2.a2.i == 26 && b2.z == 42, "");
> +#endif
> +}
> +
> +int
> +main ()
> +{
> +  if (!N1::test ()
> +      || !N2::test ()
> +      || !N3::test ()
> +      || !N4::test ()
> +      || !N5::test ()
> +      || !N7::test ()
> +      || !N9::test ()
> +      || !N10::test ()
> +      || !N12::test ()
> +      || !N13::test ()
> +      || !N14::test ())
> +    abort ();
> +}
> --- gcc/testsuite/g++.dg/cpp26/aggr-init2.C.jj	2024-07-18 13:03:47.980808005 +0200
> +++ gcc/testsuite/g++.dg/cpp26/aggr-init2.C	2024-07-18 14:31:20.930947591 +0200
> @@ -0,0 +1,67 @@
> +// P3106R1 - Clarifying rules for brace elision in aggregate initialization
> +// Examples from C++26 [dcl.init.aggr]
> +// { dg-do compile }
> +
> +namespace N1 {
> +#if __cpp_designated_initializers >= 201707L
> +  struct C {
> +    union {
> +      int a;
> +      const char* p;
> +    };
> +    int x;
> +  };
> +  constexpr C c2 = { .a = 42.0, .x = 3 };	// { dg-error "narrowing conversion of '4.2e\\\+1' from 'double' to 'int'" "" { target c++20 } }
> +#endif
> +}
> +
> +namespace N6 {
> +#if __cplusplus >= 201103L
> +  struct S {
> +    int y[] = { 0 };	// { dg-error "ISO C\\\+\\\+ forbids flexible array member 'y'" "" { target c++11 } }
> +			// { dg-error "flexible array member 'N6::S::y' in an otherwise empty 'struct N6::S' is a GCC extension" "" { target c++11 } .-1 }
> +			// { dg-error "initializer for flexible array member 'int N6::S::y \\\[\\\]'" "" { target c++11 } .-2 }
> +  };
> +#endif
> +}
> +
> +namespace N8 {
> +#if __cplusplus >= 201402L
> +  struct A;
> +  extern A a;
> +  struct A {
> +    const A &a1 { A { a, a } };
> +    const A &a2 { A { } };	// { dg-error "default member initializer for 'N8::A::a2' required before the end of its enclosing class" "" { target c++14 } }
> +  };				// { dg-error "invalid initialization of reference of type 'const N8::A\\\&' from expression of type '<brace-enclosed initializer list>'" "" { target c++14 } .-1 }
> +  A a { a, a };
> +
> +  struct B {
> +    int n = B {}.n;		// { dg-error "default member initializer for 'N8::B::n' required before the end of its enclosing class" "" { target c++14 } }
> +  };
> +
> +  struct C;
> +  extern C c;
> +  struct C {
> +    const C &c1 { C { c, c } };
> +    const C &c2 { C { c, c } };
> +  };
> +  C c { c, c };
> +#endif
> +}
> +
> +namespace N11 {
> +  char cv[4] = { 'a', 's', 'd', 'f', 0 };	// { dg-error "too many initializers for 'char \\\[4\\\]'" }
> +}
> +
> +namespace N15 {
> +  union u { int a; const char* b; };
> +  u a = { 1 };
> +  u b = a;
> +  u c = 1;                        // { dg-error "conversion from 'int' to non-scalar type 'N15::u' requested" }
> +  u d = { 0, "asdf" };            // { dg-error "too many initializers for 'N15::u'" }
> +  u e = { "asdf" };               // { dg-error "invalid conversion from 'const char\\\*' to 'int'" }
> +#if __cpp_designated_initializers >= 201707L
> +  u f = { .b = "asdf" };
> +  u g = { .a = 1, .b = "asdf" };  // { dg-error "too many initializers for 'N15::u'" "" { target c++20 } }
> +#endif
> +}
> 
> 	Jakub
>
diff mbox series

Patch

--- gcc/testsuite/g++.dg/cpp26/aggr-init1.C.jj	2024-07-18 12:25:03.767375097 +0200
+++ gcc/testsuite/g++.dg/cpp26/aggr-init1.C	2024-07-18 14:27:48.324677997 +0200
@@ -0,0 +1,341 @@ 
+// P3106R1 - Clarifying rules for brace elision in aggregate initialization
+// Examples from C++26 [dcl.init.aggr]
+// { dg-do run }
+
+extern "C" void abort ();
+
+namespace N1 {
+#if __cpp_designated_initializers >= 201707L
+  struct C {
+    union {
+      int a;
+      const char* p;
+    };
+    int x;
+  } c = { .a = 1, .x = 3 };
+  constexpr C c2 = { .a = 1, .x = 3 };
+  static_assert (c2.a == 1 && c2.x == 3, "");
+#endif
+
+  bool
+  test ()
+  {
+#if __cpp_designated_initializers >= 201707L
+    return c.a == 1 && c.x == 3;
+#else
+    return true;
+#endif
+  }
+}
+
+namespace N2 {
+  struct A {
+    int x;
+    struct B {
+      int i;
+      int j;
+    } b;
+  } a = { 1, { 2, 3 } };
+
+#if __cplusplus >= 201703L
+  struct base1 { int b1, b2 = 42; };
+  struct base2 {
+    base2 () { b3 = 42; }
+    int b3;
+  };
+  struct derived : base1, base2 {
+    int d;
+  };
+
+  derived d1 { { 1, 2 }, {}, 4 };
+  derived d2 { {}, {}, 4 };
+#endif
+
+  bool
+  test ()
+  {
+    return a.x == 1 && a.b.i == 2 && a.b.j == 3
+#if __cplusplus >= 201703L
+	   && d1.b1 == 1 && d1.b2 == 2 && d1.b3 == 42 && d1.d == 4
+	   && d2.b1 == 0 && d2.b2 == 42 && d2.b3 == 42 && d2.d == 4
+#endif
+	   ;
+  }
+
+#if __cplusplus >= 201703L
+  constexpr A a2 = { 1, { 2, 3 } };
+  static_assert (a2.x == 1 && a2.b.i == 2 && a2.b.j == 3, "");
+
+  struct base3 {
+#if __cplusplus >= 202002L
+    constexpr base3 () { b3 = 42; }
+#else
+    constexpr base3 () : b3 (42) {}
+#endif
+    int b3;
+  };
+  struct derived2 : base1, base3 {
+    int d;
+  };
+  constexpr derived2 d3 { { 1, 2}, {}, 4};
+  constexpr derived2 d4 { {}, {}, 4 };
+  static_assert (d3.b1 == 1 && d3.b2 == 2 && d3.b3 == 42 && d3.d == 4, "");
+  static_assert (d4.b1 == 0 && d4.b2 == 42 && d4.b3 == 42 && d4.d == 4, "");
+#endif
+}
+
+namespace N3 {
+#if __cplusplus >= 201402L
+  struct S { int a; const char *b; int c; int d = b[a]; };
+  S ss = { 1, "asdf" };
+  constexpr S ss2 = { 1, "asdf" };
+  static_assert (ss2.a == 1 && ss2.b[0] == 'a' && ss2.b[3] == 'f' && ss2.c == 0 && ss2.d == 's', "");
+
+#if __cpp_designated_initializers >= 201707L
+  struct string { int s = -42; };
+  struct A {
+    string a;
+    int b = 42;
+    int c = -1;
+  };
+  static_assert (A { .c = 21 }.a.s == -42 && A { .c = 21 }.b == 42 && A { .c = 21 }.c == 21, "");
+#endif
+#endif
+
+  bool
+  test ()
+  {
+#if __cplusplus >= 201402L
+    return ss.a == 1 && __builtin_strcmp (ss.b, "asdf") == 0 && ss.c == 0 && ss.d == 's';
+#else
+    return true;
+#endif
+  }
+}
+
+namespace N4 {
+  int x[] = { 1, 3, 5 };
+
+  bool
+  test ()
+  {
+    return sizeof (x) == 3 * sizeof (int) && x[0] == 1 && x[1] == 3 && x[2] == 5;
+  }
+
+#if __cplusplus >= 201103L
+  constexpr int x2[] = { 1, 3, 5 };
+  static_assert (sizeof (x2) == 3 * sizeof (int)
+		 && x2[0] == 1 && x2[1] == 3 && x2[2] == 5, "");
+#endif
+}
+
+namespace N5 {
+  struct X { int i, j, k; };
+  X a[] = { 1, 2, 3, 4, 5, 6 };
+  X b[2] = { { 1, 2, 3 }, { 4, 5, 6 } };
+
+  bool
+  test ()
+  {
+    return sizeof (a) == sizeof (b) && __builtin_memcmp (a, b, sizeof (a)) == 0;
+  }
+
+#if __cplusplus >= 201103L
+  constexpr X a2[] = { 1, 2, 3, 4, 5, 6 };
+  constexpr X b2[2] = { { 1, 2, 3 }, { 4, 5, 6 } };
+  static_assert (sizeof (a2) == 2 * sizeof (X)
+		 && a2[0].i == 1 && a2[0].j == 2 && a2[0].k == 3
+		 && a2[1].i == 4 && a2[1].j == 5 && a2[1].k == 6, "");
+  static_assert (sizeof (b2) == 2 * sizeof (X)
+		 && b2[0].i == 1 && b2[0].j == 2 && b2[0].k == 3
+		 && b2[1].i == 4 && b2[1].j == 5 && b2[1].k == 6, "");
+#endif
+}
+
+namespace N7 {
+  struct A {
+    int i;
+    static int s;
+    int j;
+    int : 17;
+    int k;
+  } a = { 1, 2, 3 };
+
+  bool
+  test ()
+  {
+    return a.i == 1 && a.j == 2 && a.k == 3;
+  }
+
+#if __cplusplus >= 201103L
+  constexpr A a2 = { 1, 2, 3 };
+  static_assert (a2.i == 1 && a2.j == 2 && a2.k == 3, "");
+#endif
+}
+
+namespace N9 {
+  int x[2][2] = { 3, 1, 4, 2 };
+  float y[4][3] = {
+    { 1 }, { 2 }, { 3 }, { 4 }
+  };
+
+  bool
+  test ()
+  {
+    return x[0][0] == 3 && x[0][1] == 1 && x[1][0] == 4 && x[1][1] == 2
+	   && y[0][0] == 1.f && y[0][1] == 0.f && y[0][2] == 0.f
+	   && y[1][0] == 2.f && y[1][1] == 0.f && y[1][2] == 0.f
+	   && y[2][0] == 3.f && y[2][1] == 0.f && y[2][2] == 0.f
+           && y[3][0] == 4.f && y[3][1] == 0.f && y[3][2] == 0.f;
+  }
+
+#if __cplusplus >= 201103L
+  constexpr int x2[2][2] = { 3, 1, 4, 2 };
+  constexpr float y2[4][3] = {
+    { 1 }, { 2 }, { 3 }, { 4 }
+  };
+  static_assert (x2[0][0] == 3 && x2[0][1] == 1 && x2[1][0] == 4 && x2[1][1] == 2, "");
+  static_assert (y2[0][0] == 1.f && y2[0][1] == 0.f && y2[0][2] == 0.f, "");
+  static_assert (y2[1][0] == 2.f && y2[1][1] == 0.f && y2[1][2] == 0.f, "");
+  static_assert (y2[2][0] == 3.f && y2[2][1] == 0.f && y2[2][2] == 0.f, "");
+  static_assert (y2[3][0] == 4.f && y2[3][1] == 0.f && y2[3][2] == 0.f, "");
+#endif
+}
+
+namespace N10 {
+  struct S1 { int a, b; };
+  struct S2 { S1 s, t; };
+
+  S2 x[2] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+  S2 y[2] = {
+    {
+      { 1, 2 },
+      { 3, 4 }
+    },
+    {
+      { 5, 6 },
+      { 7, 8 }
+    }
+  };
+
+  bool
+  test ()
+  {
+    return sizeof (x) == sizeof (y) && __builtin_memcmp (x, y, sizeof (x)) == 0;
+  }
+
+#if __cplusplus >= 201103L
+  constexpr S2 x2[2] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+  constexpr S2 y2[2] = { { { 1, 2 }, { 3, 4 } }, { { 5, 6 }, { 7, 8 } } };
+  static_assert (x2[0].s.a == 1 && x2[0].s.b == 2 && x2[0].t.a == 3 && x2[0].t.b == 4, "");
+  static_assert (x2[1].s.a == 5 && x2[1].s.b == 6 && x2[1].t.a == 7 && x2[1].t.b == 8, "");
+  static_assert (y2[0].s.a == 1 && y2[0].s.b == 2 && y2[0].t.a == 3 && y2[0].t.b == 4, "");
+  static_assert (y2[1].s.a == 5 && y2[1].s.b == 6 && y2[1].t.a == 7 && y2[1].t.b == 8, "");
+#endif
+}
+
+namespace N12 {
+  float y[4][3] = {
+    { 1, 3, 5 },
+    { 2, 4, 6 },
+    { 3, 5, 7 },
+  };
+  float y2[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 };
+
+  bool
+  test ()
+  {
+    for (int i = 0; i < 4; ++i)
+      for (int j = 0; j < 3; ++j)
+	if (y[i][j] != (i == 3 ? 0.f : (float) (i + 1 + j * 2))
+	    || y[i][j] != y2[i][j])
+	  return false;
+    return true;
+  }
+
+#if __cplusplus >= 201103L
+  constexpr float y3[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 }, };
+  constexpr float y4[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 };
+  static_assert (y3[0][0] == 1.f && y3[0][1] == 3.f && y3[0][2] == 5.f, "");
+  static_assert (y3[1][0] == 2.f && y3[1][1] == 4.f && y3[1][2] == 6.f, "");
+  static_assert (y3[2][0] == 3.f && y3[2][1] == 5.f && y3[2][2] == 7.f, "");
+  static_assert (y3[3][0] == 0.f && y3[3][1] == 0.f && y3[3][2] == 0.f, "");
+  static_assert (y4[0][0] == 1.f && y4[0][1] == 3.f && y4[0][2] == 5.f, "");
+  static_assert (y4[1][0] == 2.f && y4[1][1] == 4.f && y4[1][2] == 6.f, "");
+  static_assert (y4[2][0] == 3.f && y4[2][1] == 5.f && y4[2][2] == 7.f, "");
+  static_assert (y4[3][0] == 0.f && y4[3][1] == 0.f && y4[3][2] == 0.f, "");
+#endif
+}
+
+namespace N13 {
+  bool
+  test ()
+  {
+    struct S { } s;
+    struct A {
+      S s1;
+      int i1;
+      S s2;
+      int i2;
+      S s3;
+      int i3;
+    } a = {
+      { },
+      0,
+      s,
+      0
+    };
+    return a.i1 == 0 && a.i2 == 0;
+  }
+}
+
+namespace N14 {
+  struct A {
+    int i;
+    operator int ();
+  };
+  struct B {
+    A a1, a2;
+    int z;
+  };
+  A a;
+  B b = { 4, a, a };
+  A::operator int () { return 42; }
+
+  bool
+  test ()
+  {
+    return b.a1.i == 4 && b.a2.i == 0 && b.z == 42;
+  }
+
+#if __cplusplus >= 201103L
+  struct A2 {
+    int i;
+    constexpr operator int () const { return 42; }
+  };
+  struct B2 {
+    A2 a1, a2;
+    int z;
+  };
+  constexpr A2 a2 = { 26 };
+  constexpr B2 b2 = { 4, a2, a2 };
+  static_assert (b2.a1.i == 4 && b2.a2.i == 26 && b2.z == 42, "");
+#endif
+}
+
+int
+main ()
+{
+  if (!N1::test ()
+      || !N2::test ()
+      || !N3::test ()
+      || !N4::test ()
+      || !N5::test ()
+      || !N7::test ()
+      || !N9::test ()
+      || !N10::test ()
+      || !N12::test ()
+      || !N13::test ()
+      || !N14::test ())
+    abort ();
+}
--- gcc/testsuite/g++.dg/cpp26/aggr-init2.C.jj	2024-07-18 13:03:47.980808005 +0200
+++ gcc/testsuite/g++.dg/cpp26/aggr-init2.C	2024-07-18 14:31:20.930947591 +0200
@@ -0,0 +1,67 @@ 
+// P3106R1 - Clarifying rules for brace elision in aggregate initialization
+// Examples from C++26 [dcl.init.aggr]
+// { dg-do compile }
+
+namespace N1 {
+#if __cpp_designated_initializers >= 201707L
+  struct C {
+    union {
+      int a;
+      const char* p;
+    };
+    int x;
+  };
+  constexpr C c2 = { .a = 42.0, .x = 3 };	// { dg-error "narrowing conversion of '4.2e\\\+1' from 'double' to 'int'" "" { target c++20 } }
+#endif
+}
+
+namespace N6 {
+#if __cplusplus >= 201103L
+  struct S {
+    int y[] = { 0 };	// { dg-error "ISO C\\\+\\\+ forbids flexible array member 'y'" "" { target c++11 } }
+			// { dg-error "flexible array member 'N6::S::y' in an otherwise empty 'struct N6::S' is a GCC extension" "" { target c++11 } .-1 }
+			// { dg-error "initializer for flexible array member 'int N6::S::y \\\[\\\]'" "" { target c++11 } .-2 }
+  };
+#endif
+}
+
+namespace N8 {
+#if __cplusplus >= 201402L
+  struct A;
+  extern A a;
+  struct A {
+    const A &a1 { A { a, a } };
+    const A &a2 { A { } };	// { dg-error "default member initializer for 'N8::A::a2' required before the end of its enclosing class" "" { target c++14 } }
+  };				// { dg-error "invalid initialization of reference of type 'const N8::A\\\&' from expression of type '<brace-enclosed initializer list>'" "" { target c++14 } .-1 }
+  A a { a, a };
+
+  struct B {
+    int n = B {}.n;		// { dg-error "default member initializer for 'N8::B::n' required before the end of its enclosing class" "" { target c++14 } }
+  };
+
+  struct C;
+  extern C c;
+  struct C {
+    const C &c1 { C { c, c } };
+    const C &c2 { C { c, c } };
+  };
+  C c { c, c };
+#endif
+}
+
+namespace N11 {
+  char cv[4] = { 'a', 's', 'd', 'f', 0 };	// { dg-error "too many initializers for 'char \\\[4\\\]'" }
+}
+
+namespace N15 {
+  union u { int a; const char* b; };
+  u a = { 1 };
+  u b = a;
+  u c = 1;                        // { dg-error "conversion from 'int' to non-scalar type 'N15::u' requested" }
+  u d = { 0, "asdf" };            // { dg-error "too many initializers for 'N15::u'" }
+  u e = { "asdf" };               // { dg-error "invalid conversion from 'const char\\\*' to 'int'" }
+#if __cpp_designated_initializers >= 201707L
+  u f = { .b = "asdf" };
+  u g = { .a = 1, .b = "asdf" };  // { dg-error "too many initializers for 'N15::u'" "" { target c++20 } }
+#endif
+}