+ if (sec.get_overrun ())
+ break;
+ decomp_phase = tree_to_shwi (decl);
+ if (decomp_phase)
+ last = decomp_phase > 2 ? tls_aggregates : static_aggregates;
+ decl = sec.tree_node ();
+ }
if (sec.get_overrun ())
break;
if (decl)
- dump ("Initializer:%u for %N", count, decl);
+ dump ("Initializer:%u for %N", ix, decl);
+ if (decomp_phase)
+ {
+ tree init = decomp_phase > 2 ? tls_aggregates : static_aggregates;
+ gcc_assert (TREE_VALUE (init) == decl && TREE_CHAIN (init) == last);
+ if ((decomp_phase & 1) != 0)
+ STATIC_INIT_DECOMP_BASE_P (init) = 1;
+ else
+ STATIC_INIT_DECOMP_NONBASE_P (init) = 1;
+ }
+ }
+ if (decomp_phase && !sec.get_overrun ())
+ {
+ tree decl = sec.tree_node ();
+ gcc_assert (integer_zerop (decl));
}
lazy_snum = 0;
post_load_processing ();
--- gcc/testsuite/g++.dg/modules/dr2867-1_a.H.jj 2025-01-21
10:53:50.426106945 +0100
+++ gcc/testsuite/g++.dg/modules/dr2867-1_a.H 2025-01-21 10:53:50.426106945
+0100
@@ -0,0 +1,88 @@
+// CWG2867 - Order of initialization for structured bindings.
+// { dg-additional-options -fmodule-header }
+// { dg-module-cmi {} }
+
+#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
+
+namespace std {
+ template<typename T> struct tuple_size;
+ template<int, typename> struct tuple_element;
+}
+
+extern int a, c, d, i;
+
+struct A {
+ A () { assert (c == 3); ++c; }
+ ~A () { ++a; }
+ template <int I> int &get () const { assert (c == 5 + I); ++c; return i; }
+};
+
+template <> struct std::tuple_size <A> { static const int value = 4; };
+template <int I> struct std::tuple_element <I, A> { using type = int; };
+template <> struct std::tuple_size <const A> { static const int value = 4; };
+template <int I> struct std::tuple_element <I, const A> { using type = int; };
+
+struct B {
+ B () { assert (c >= 1 && c <= 2); ++c; }
+ ~B () { assert (c >= 9 && c <= 10); ++c; }
+};
+
+struct C {
+ constexpr C () {}
+ constexpr C (const C &) {}
+ template <int I> int &get () const { assert (d == 1 + I); ++d; return i; }
+};
+
+template <> struct std::tuple_size <C> { static const int value = 3; };
+template <int I> struct std::tuple_element <I, C> { using type = int; };
+template <> struct std::tuple_size <const C> { static const int value = 3; };
+template <int I> struct std::tuple_element <I, const C> { using type = int; };
+
+inline A
+foo (const B &, const B &)
+{
+ A a;
+ assert (c == 4);
+ ++c;
+ return a;
+}
+
+constexpr C
+foo (const C &, const C &)
+{
+ return C {};
+}
+
+inline int
+bar (int &x, int y)
+{
+ x = y;
+ return y;
+}
+
+inline int
+baz (int &x, int y)
+{
+ assert (x == y);
+ return y;
+}
+
+struct E {
+ ~E () { assert (a == 2); }
+};
+
+namespace {
+E e;
+int c1 = bar (c, 1);
+const auto &[x, y, z, w] = foo (B {}, B {});
+int c2 = baz (c, 11);
+int d1 = bar (d, 1);
+const auto &[s, t, u] = foo (C {}, C {});
+int d2 = baz (d, 4);
+int c3 = bar (c, 1);
+auto [x2, y2, z2, w2] = foo (B {}, B {});
+int c4 = baz (c, 11);
+int d3 = bar (d, 1);
+auto [s2, t2, u2] = foo (C {}, C {});
+int d4 = baz (d, 4);
+}
--- gcc/testsuite/g++.dg/modules/dr2867-1_b.C.jj 2025-01-21
10:53:50.426106945 +0100
+++ gcc/testsuite/g++.dg/modules/dr2867-1_b.C 2025-01-21 10:53:50.426106945
+0100
@@ -0,0 +1,13 @@
+// CWG2867 - Order of initialization for structured bindings.
+// { dg-do run }
+// { dg-additional-options "-fmodules-ts" }
+
+import "dr2867-1_a.H";
+
+int a, c, d, i;
+
+int
+main ()
+{
+ assert (a == 0);
+}
--- gcc/testsuite/g++.dg/modules/dr2867-2_a.H.jj 2025-01-21
13:46:28.994587192 +0100
+++ gcc/testsuite/g++.dg/modules/dr2867-2_a.H 2025-01-21 13:47:55.035390907
+0100
@@ -0,0 +1,79 @@
+// CWG2867 - Order of initialization for structured bindings.
+// { dg-additional-options -fmodule-header }
+// { dg-module-cmi {} }
+
+#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
+
+namespace std {
+ template<typename T> struct tuple_size;
+ template<int, typename> struct tuple_element;
+}
+
+extern int a, c;
+
+struct C {
+ C () { assert (c >= 5 && c <= 17 && (c - 5) % 4 == 0); ++c; }
+ ~C () { assert (c >= 8 && c <= 20 && c % 4 == 0); ++c; }
+};
+
+struct D {
+ D () { assert (c >= 7 && c <= 19 && (c - 7) % 4 == 0); ++c; }
+ ~D () { assert (a % 5 != 4); ++a; }
+};
+
+struct A {
+ A () { assert (c == 3); ++c; }
+ ~A () { assert (a % 5 == 4); ++a; }
+ template <int I> D get (const C & = C{}) const { assert (c == 6 + 4 * I);
++c; return D {}; }
+};
+
+template <> struct std::tuple_size <A> { static const int value = 4; };
+template <int I> struct std::tuple_element <I, A> { using type = D; };
+template <> struct std::tuple_size <const A> { static const int value = 4; };
+template <int I> struct std::tuple_element <I, const A> { using type = D; };
+
+struct B {
+ B () { assert (c >= 1 && c <= 2); ++c; }
+ ~B () { assert (c >= 21 && c <= 22); ++c; }
+};
+
+inline A
+foo (const B &, const B &)
+{
+ A a;
+ assert (c == 4);
+ ++c;
+ return a;
+}
+
+inline int
+bar (int &x, int y)
+{
+ x = y;
+ return y;
+}
+
+inline int
+baz (int &x, int y)
+{
+ assert (x == y);
+ return y;
+}
+
+struct E {
+ ~E () { assert (a == 5); }
+};
+
+namespace {
+E e;
+int c1 = bar (c, 1);
+// First B::B () is invoked twice, then foo called, which invokes A::A ().
+// e is reference bound to the A::A () constructed temporary.
+// Then 4 times (in increasing I):
+// C::C () is invoked, get is called, D::D () is invoked, C::~C () is
+// invoked.
+// After that B::~B () is invoked twice.
+// At exit time D::~D () is invoked 4 times, then A::~A ().
+const auto &[x, y, z, w] = foo (B {}, B {});
+int c2 = baz (c, 23);
+}
--- gcc/testsuite/g++.dg/modules/dr2867-2_b.C.jj 2025-01-21
13:48:38.889781164 +0100
+++ gcc/testsuite/g++.dg/modules/dr2867-2_b.C 2025-01-21 13:48:33.295858937
+0100
@@ -0,0 +1,13 @@
+// CWG2867 - Order of initialization for structured bindings.
+// { dg-do run }
+// { dg-additional-options "-fmodules-ts" }
+
+import "dr2867-2_a.H";
+
+int a, c;
+
+int
+main ()
+{
+ assert (a == 0);
+}
--- gcc/testsuite/g++.dg/modules/dr2867-3_a.H.jj 2025-01-21
13:53:26.897776305 +0100
+++ gcc/testsuite/g++.dg/modules/dr2867-3_a.H 2025-01-21 14:01:56.025695002
+0100
@@ -0,0 +1,91 @@
+// CWG2867 - Order of initialization for structured bindings.
+// { dg-require-effective-target c++20 }
+// { dg-additional-options -fmodule-header }
+// { dg-module-cmi {} }
+// { dg-add-options tls }
+// { dg-require-effective-target tls_runtime }
+
+#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
+
+namespace std {
+ template<typename T> struct tuple_size;
+ template<int, typename> struct tuple_element;
+}
+
+extern int a, c, d, i;
+
+struct A {
+ A () { assert (c == 3); ++c; }
+ ~A () { ++a; }
+ template <int I> int &get () const { assert (c == 5 + I); ++c; return i; }
+};
+
+template <> struct std::tuple_size <A> { static const int value = 4; };
+template <int I> struct std::tuple_element <I, A> { using type = int; };
+template <> struct std::tuple_size <const A> { static const int value = 4; };
+template <int I> struct std::tuple_element <I, const A> { using type = int; };
+
+struct B {
+ B () { assert (c >= 1 && c <= 2); ++c; }
+ ~B () { assert (c >= 9 && c <= 10); ++c; }
+};
+
+struct C {
+ constexpr C () {}
+ constexpr C (const C &) {}
+ template <int I> int &get () const { assert (d == 1 + I); ++d; return i; }
+};
+
+template <> struct std::tuple_size <C> { static const int value = 3; };
+template <int I> struct std::tuple_element <I, C> { using type = int; };
+template <> struct std::tuple_size <const C> { static const int value = 3; };
+template <int I> struct std::tuple_element <I, const C> { using type = int; };
+
+inline A
+foo (const B &, const B &)
+{
+ A a;
+ assert (c == 4);
+ ++c;
+ return a;
+}
+
+constexpr C
+foo (const C &, const C &)
+{
+ return C {};
+}
+
+inline int
+bar (int &x, int y)
+{
+ x = y;
+ return y;
+}
+
+inline int
+baz (int &x, int y)
+{
+ assert (x == y);
+ return y;
+}
+
+struct E {
+ ~E () { assert (a == 2); }
+};
+
+namespace {
+thread_local E e;
+thread_local int c1 = bar (c, 1);
+thread_local const auto &[x, y, z, w] = foo (B {}, B {});
+thread_local int c2 = baz (c, 11);
+thread_local int d1 = bar (d, 1);
+thread_local const auto &[s, t, u] = foo (C {}, C {});
+thread_local int d2 = baz (d, 4);
+thread_local int c3 = bar (c, 1);
+thread_local auto [x2, y2, z2, w2] = foo (B {}, B {});
+thread_local int c4 = baz (c, 11);
+thread_local int d3 = bar (d, 1);
+thread_local auto [s2, t2, u2] = foo (C {}, C {});
+thread_local int d4 = baz (d, 4);
+}
--- gcc/testsuite/g++.dg/modules/dr2867-3_b.C.jj 2025-01-21
13:56:51.387932118 +0100
+++ gcc/testsuite/g++.dg/modules/dr2867-3_b.C 2025-01-21 14:02:25.521284755
+0100
@@ -0,0 +1,19 @@
+// CWG2867 - Order of initialization for structured bindings.
+// { dg-do run { target c++20 } }
+// { dg-additional-options "-fmodules-ts" }
+// { dg-add-options tls }
+// { dg-require-effective-target tls_runtime }
+
+import "dr2867-3_a.H";
+
+int a, c, d, i;
+
+int
+main ()
+{
+ volatile int u = c1 + x + y + z + w + c2;
+ u += d1 + s + t + u + d2;
+ u += c3 + x2 + y2 + z2 + w2 + c4;
+ u += d3 + s2 + t2 + u2 + d4;
+ assert (a == 0);
+}
--- gcc/testsuite/g++.dg/modules/dr2867-4_a.H.jj 2025-01-21
13:53:30.007733050 +0100
+++ gcc/testsuite/g++.dg/modules/dr2867-4_a.H 2025-01-21 14:02:08.828516934
+0100
@@ -0,0 +1,82 @@
+// CWG2867 - Order of initialization for structured bindings.
+// { dg-require-effective-target c++20 }
+// { dg-additional-options -fmodule-header }
+// { dg-module-cmi {} }
+// { dg-add-options tls }
+// { dg-require-effective-target tls_runtime }
+
+#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
+
+namespace std {
+ template<typename T> struct tuple_size;
+ template<int, typename> struct tuple_element;
+}
+
+extern int a, c;
+
+struct C {
+ C () { assert (c >= 5 && c <= 17 && (c - 5) % 4 == 0); ++c; }
+ ~C () { assert (c >= 8 && c <= 20 && c % 4 == 0); ++c; }
+};
+
+struct D {
+ D () { assert (c >= 7 && c <= 19 && (c - 7) % 4 == 0); ++c; }
+ ~D () { assert (a % 5 != 4); ++a; }
+};
+
+struct A {
+ A () { assert (c == 3); ++c; }
+ ~A () { assert (a % 5 == 4); ++a; }
+ template <int I> D get (const C & = C{}) const { assert (c == 6 + 4 * I);
++c; return D {}; }
+};
+
+template <> struct std::tuple_size <A> { static const int value = 4; };
+template <int I> struct std::tuple_element <I, A> { using type = D; };
+template <> struct std::tuple_size <const A> { static const int value = 4; };
+template <int I> struct std::tuple_element <I, const A> { using type = D; };
+
+struct B {
+ B () { assert (c >= 1 && c <= 2); ++c; }
+ ~B () { assert (c >= 21 && c <= 22); ++c; }
+};
+
+inline A
+foo (const B &, const B &)
+{
+ A a;
+ assert (c == 4);
+ ++c;
+ return a;
+}
+
+inline int
+bar (int &x, int y)
+{
+ x = y;
+ return y;
+}
+
+inline int
+baz (int &x, int y)
+{
+ assert (x == y);
+ return y;
+}
+
+struct E {
+ ~E () { assert (a == 5); }
+};
+
+namespace {
+thread_local E e;
+thread_local int c1 = bar (c, 1);
+// First B::B () is invoked twice, then foo called, which invokes A::A ().
+// e is reference bound to the A::A () constructed temporary.
+// Then 4 times (in increasing I):
+// C::C () is invoked, get is called, D::D () is invoked, C::~C () is
+// invoked.
+// After that B::~B () is invoked twice.
+// At exit time D::~D () is invoked 4 times, then A::~A ().
+thread_local const auto &[x, y, z, w] = foo (B {}, B {});
+thread_local int c2 = baz (c, 23);
+}
--- gcc/testsuite/g++.dg/modules/dr2867-4_b.C.jj 2025-01-21
13:59:54.693382577 +0100
+++ gcc/testsuite/g++.dg/modules/dr2867-4_b.C 2025-01-21 14:02:54.089887406
+0100
@@ -0,0 +1,16 @@
+// CWG2867 - Order of initialization for structured bindings.
+// { dg-do run { target c++20 } }
+// { dg-additional-options "-fmodules-ts" }
+// { dg-add-options tls }
+// { dg-require-effective-target tls_runtime }
+
+import "dr2867-4_a.H";
+
+int a, c;
+
+int
+main ()
+{
+ volatile int u = c1 + c2;
+ assert (a == 0);
+}
Jakub