for (tree init = list; init; init = TREE_CHAIN (init))
if (TREE_LANG_FLAG_0 (init))
{
+ if (STATIC_INIT_DECOMP_BASE_P (init))
+ {
+ /* Ensure that in the returned result chain if the
+ STATIC_INIT_DECOMP_*BASE_P flags are set, there is
+ always one or more STATIC_INIT_DECOMP_BASE_P TREE_LIST
+ followed by one or more STATIC_INIT_DECOMP_NONBASE_P. */
+ int phase = 0;
+ tree last = NULL_TREE;
+ for (tree init2 = TREE_CHAIN (init);
+ init2; init2 = TREE_CHAIN (init2))
+ {
+ if (phase == 0 && STATIC_INIT_DECOMP_BASE_P (init2))
+ ;
+ else if (phase == 0
+ && STATIC_INIT_DECOMP_NONBASE_P (init2))
+ {
+ phase = TREE_LANG_FLAG_0 (init2) ? 2 : 1;
+ last = init2;
+ }
+ else if (IN_RANGE (phase, 1, 2)
+ && STATIC_INIT_DECOMP_NONBASE_P (init2))
+ {
+ if (TREE_LANG_FLAG_0 (init2))
+ phase = 2;
+ last = init2;
+ }
+ else
+ break;
+ }
+ if (phase == 2)
+ {
+ /* In that case, add markers about it so that the
+ STATIC_INIT_DECOMP_BASE_P and
+ STATIC_INIT_DECOMP_NONBASE_P flags can be restored. */
+ sec.tree_node (build_int_cst (integer_type_node,
+ 2 * passes + 1));
+ phase = 1;
+ for (tree init2 = init; init2 != TREE_CHAIN (last);
+ init2 = TREE_CHAIN (init2))
+ if (TREE_LANG_FLAG_0 (init2))
+ {
+ tree decl = TREE_VALUE (init2);
+ if (phase == 1
+ && STATIC_INIT_DECOMP_NONBASE_P (init2))
+ {
+ sec.tree_node (build_int_cst (integer_type_node,
+ 2 * passes + 2));
+ phase = 2;
+ }
+ dump ("Initializer:%u for %N", count, decl);
+ sec.tree_node (decl);
+ ++count;
+ }
+ sec.tree_node (integer_zero_node);
+ init = last;
+ continue;
+ }
+ }
+
tree decl = TREE_VALUE (init);
dump ("Initializer:%u for %N", count, decl);
dump.indent ();
lazy_snum = ~0u;
+ int decomp_phase = 0;
+ tree *aggrp = NULL;
for (unsigned ix = 0; ix != count; ix++)
{
+ tree last = NULL_TREE;
+ if (decomp_phase)
+ last = *aggrp;
/* Merely referencing the decl causes its initializer to be read
and added to the correct list. */
tree decl = sec.tree_node ();
+ /* module_state::write_inits can add special INTEGER_CST markers in
+ between the decls. 1 means STATIC_INIT_DECOMP_BASE_P entries
+ follow in static_aggregates, 2 means STATIC_INIT_DECOMP_NONBASE_P
+ entries follow in static_aggregates, 3 means
+ STATIC_INIT_DECOMP_BASE_P entries follow in tls_aggregates,
+ 4 means STATIC_INIT_DECOMP_NONBASE_P follow in tls_aggregates,
+ 0 means end of STATIC_INIT_DECOMP_{,NON}BASE_P sequence. */
+ if (tree_fits_shwi_p (decl))
+ {
+ if (sec.get_overrun ())
+ break;
+ decomp_phase = tree_to_shwi (decl);
+ if (decomp_phase)
+ {
+ aggrp = decomp_phase > 2 ? &tls_aggregates : &static_aggregates;
+ last = *aggrp;
+ }
+ 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 = *aggrp;
+ 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 ();
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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);
+}