On 9/6/24 8:02 AM, Jakub Jelinek wrote:
Hi!

On Wed, Aug 14, 2024 at 06:11:35PM +0200, Jakub Jelinek wrote:
Here is the I believe ABI compatible version, which uses the separate
guard variables, so different structured binding variables can be
initialized in different threads, but the thread that did the artificial
base initialization will keep temporaries live at least until the last
guard variable is released (i.e. when even that variable has been
initialized).

Bootstrapped/regtested on x86_64-linux and i686-linux on top of the
https://gcc.gnu.org/pipermail/gcc-patches/2024-August/660354.html
patch, ok for trunk?

As for namespace scope structured bindings and this DR, all of
set_up_extended_ref_temp, cp_finish_decl -> expand_static_init and
cp_finish_decl -> cp_finish_decomp -> cp_finish_decl -> expand_static_init
in that case just push some decls into the static_aggregates or
tls_aggregates chains.
So, we can end up e.g. with the most important decl for a extended ref
temporary (which initializes some temporaries), then perhaps some more
of those, then DECL_DECOMPOSITION_P base, then n times optionally some further
extended refs and DECL_DECOMPOSITION_P non-base and I think we need
to one_static_initialization_or_destruction all of them together, by
omitting CLEANUP_POINT_EXPR from the very first one (or all until the
DECL_DECOMPOSITION_P base?), say through temporarily clearing
stmts_are_full_exprs_p and then wrapping whatever
one_static_initialization_or_destruction produces for all of those into
a single CLEANUP_POINT_EXPR argument.
Perhaps remember static_aggregates or tls_aggregates early before any
check_initializer etc. calls and then after cp_finish_decomp cut that
TREE_LIST nodes and pass that as a separate TREE_VALUE in the list.
Though, not sure what to do about modules.cc uses of these, it needs
to save/restore that stuff somehow too.

Now that the CWG 2867 patch for automatic structured bindings is in,
here is an updated version of the block scope static structured bindings
CWG 2867 patch.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.

No patch for the namespace scope structured bindings yet, will work on that
soon.

2024-09-05  Jakub Jelinek  <ja...@redhat.com>

        PR c++/115769
        * decl.cc: Partially implement CWG 2867 - Order of initialization
        for structured bindings.
        (cp_finish_decl): If need_decomp_init, for function scope structure
        binding bases, temporarily clear stmts_are_full_exprs_p before
        calling expand_static_init, after it call cp_finish_decomp and wrap
        code emitted by both into maybe_cleanup_point_expr_void and ensure
        cp_finish_decomp isn't called again.

        * g++.dg/DRs/dr2867-3.C: New test.
        * g++.dg/DRs/dr2867-4.C: New test.

--- gcc/cp/decl.cc.jj   2024-09-04 19:55:59.046491602 +0200
+++ gcc/cp/decl.cc      2024-09-04 20:04:35.695952219 +0200
@@ -9140,7 +9140,24 @@ cp_finish_decl (tree decl, tree init, bo
         initializer.  It is not legal to redeclare a static data
         member, so this issue does not arise in that case.  */
        else if (var_definition_p && TREE_STATIC (decl))
-       expand_static_init (decl, init);
+       {
+         if (decomp && DECL_FUNCTION_SCOPE_P (decl))
+           {
+             tree sl = push_stmt_list ();
+             auto saved_stmts_are_full_exprs_p = stmts_are_full_exprs_p ();
+             current_stmt_tree ()->stmts_are_full_exprs_p = 0;
+             expand_static_init (decl, init);
+             current_stmt_tree ()->stmts_are_full_exprs_p
+               = saved_stmts_are_full_exprs_p;
+             cp_finish_decomp (decl, decomp);
+             decomp = NULL;
+             sl = pop_stmt_list (sl);
+             sl = maybe_cleanup_point_expr_void (sl);
+             add_stmt (sl);
+           }
+         else
+           expand_static_init (decl, init);
+       }
      }
/* If a CLEANUP_STMT was created to destroy a temporary bound to a
--- gcc/testsuite/g++.dg/DRs/dr2867-3.C.jj      2024-08-13 21:05:42.876446125 
+0200
+++ gcc/testsuite/g++.dg/DRs/dr2867-3.C 2024-08-13 21:05:42.876446125 +0200
@@ -0,0 +1,159 @@
+// CWG2867 - Order of initialization for structured bindings.
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
+
+namespace std {
+  template<typename T> struct tuple_size;
+  template<int, typename> struct tuple_element;
+}
+
+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; };
+
+A
+foo (const B &, const B &)
+{
+  A a;
+  assert (c == 4);
+  ++c;
+  return a;
+}
+
+constexpr C
+foo (const C &, const C &)
+{
+  return C {};
+}
+
+int
+foo (const int &, const int &)
+{
+  assert (false);
+}
+
+inline void
+bar ()
+{
+  c = 1;
+  static const auto &[x, y, z, w] = foo (B {}, B {});      // { dg-warning "structured 
bindings only available with" "" { target c++14_down } }
+  assert (c == 11);                                    // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  ++c;
+  d = 1;
+  static const auto &[s, t, u] = foo (C {}, C {}); // { dg-warning "structured bindings only 
available with" "" { target c++14_down } }
+  assert (d == 4);                                     // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+}
+
+template <int N>
+inline void
+baz ()
+{
+  c = 1;
+  static const auto &[x, y, z, w] = foo (B {}, B {});      // { dg-warning "structured 
bindings only available with" "" { target c++14_down } }
+  assert (c == 11);                                    // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  ++c;
+  d = 1;
+  static const auto &[s, t, u] = foo (C {}, C {}); // { dg-warning "structured bindings only 
available with" "" { target c++14_down } }
+  assert (d == 4);                                     // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+}
+
+template <typename T, typename U>
+inline void
+qux ()
+{
+  c = 1;
+  static const auto &[x, y, z, w] = foo (T {}, T {});      // { dg-warning "structured 
bindings only available with" "" { target c++14_down } }
+  assert (c == 11);                                    // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  ++c;
+  d = 1;
+  static const auto &[s, t, u] = foo (U {}, U {}); // { dg-warning "structured bindings only 
available with" "" { target c++14_down } }
+  assert (d == 4);                                     // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+}
+
+inline void
+corge ()
+{
+  c = 1;
+  static auto [x, y, z, w] = foo (B {}, B {}); // { dg-warning "structured bindings only 
available with" "" { target c++14_down } }
+  assert (c == 11);                            // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  ++c;
+  d = 1;
+  static auto [s, t, u] = foo (C {}, C {});    // { dg-warning "structured bindings only 
available with" "" { target c++14_down } }
+  assert (d == 4);                             // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+}
+
+template <int N>
+inline void
+garply ()
+{
+  c = 1;
+  static auto [x, y, z, w] = foo (B {}, B {}); // { dg-warning "structured bindings only 
available with" "" { target c++14_down } }
+  assert (c == 11);                            // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  ++c;
+  d = 1;
+  static auto [s, t, u] = foo (C {}, C {});    // { dg-warning "structured bindings only 
available with" "" { target c++14_down } }
+  assert (d == 4);                             // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+}
+
+template <typename T, typename U>
+inline void
+freddy ()
+{
+  c = 1;
+  static auto [x, y, z, w] = foo (T {}, T {}); // { dg-warning "structured bindings only 
available with" "" { target c++14_down } }
+  assert (c == 11);                            // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  ++c;
+  d = 1;
+  static auto [s, t, u] = foo (U {}, U {});    // { dg-warning "structured bindings only 
available with" "" { target c++14_down } }
+  assert (d == 4);                             // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+}
+
+struct E {
+  ~E () { assert (a == 6); }
+};
+
+int
+main ()
+{
+  static E e;
+  bar ();
+  assert (c == 12);
+  baz <0> ();
+  assert (c == 12);
+  qux <B, C> ();
+  assert (c == 12);
+  corge ();
+  assert (c == 12);
+  garply <42> ();
+  assert (c == 12);
+  freddy <B, C> ();
+  assert (c == 12);
+  assert (a == 0);
+}
--- gcc/testsuite/g++.dg/DRs/dr2867-4.C.jj      2024-08-13 21:05:42.876446125 
+0200
+++ gcc/testsuite/g++.dg/DRs/dr2867-4.C 2024-08-13 21:05:42.876446125 +0200
@@ -0,0 +1,108 @@
+// CWG2867 - Order of initialization for structured bindings.
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
+
+namespace std {
+  template<typename T> struct tuple_size;
+  template<int, typename> struct tuple_element;
+}
+
+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; }
+};
+
+A
+foo (const B &, const B &)
+{
+  A a;
+  assert (c == 4);
+  ++c;
+  return a;
+}
+
+int
+foo (const int &, const int &)
+{
+  assert (false);
+}
+
+inline void
+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, then the following 2 user
+  // statements.
+  // At exit time D::~D () is invoked 4 times, then A::~A (), repeated 3
+  // times.
+  static const auto &[x, y, z, w] = foo (B {}, B {});      // { dg-warning "structured 
bindings only available with" "" { target c++14_down } }
+  assert (c == 23);                                    // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  ++c;
+}
+
+template <int N>
+inline void
+baz ()
+{
+  c = 1;
+  static const auto &[x, y, z, w] = foo (B {}, B {});      // { dg-warning "structured 
bindings only available with" "" { target c++14_down } }
+  assert (c == 23);                                    // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  ++c;
+}
+
+template <typename T>
+inline void
+qux ()
+{
+  c = 1;
+  static const auto &[x, y, z, w] = foo (T {}, T {});      // { dg-warning "structured 
bindings only available with" "" { target c++14_down } }
+  assert (c == 23);                                    // { dg-warning "structured binding 
declaration can be 'static' only in" "" { target c++17_down } .-1 }
+  ++c;
+}
+
+struct E {
+  ~E () { assert (a == 15); }
+};
+
+int
+main ()
+{
+  static E e;
+  bar ();
+  assert (c == 24);
+  baz <42> ();
+  assert (c == 24);
+  qux <B> ();
+  assert (c == 24);
+  assert (a == 0);
+}


        Jakub


Reply via email to