https://gcc.gnu.org/g:6db9d4e954bff3dfd926c7c9b71e41e47b7089c8

commit r15-7128-g6db9d4e954bff3dfd926c7c9b71e41e47b7089c8
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Wed Jan 22 19:36:36 2025 +0100

    c++: Implement for static locals CWG 2867 - Order of initialization for 
structured bindings [PR115769]
    
    On Wed, Aug 14, 2024 at 10:06:24AM +0200, Jakub Jelinek wrote:
    > Though, now that I think about it again, perhaps what we could do instead
    > is just make sure the _ZGVZ3barvEDC1x1y1z1wE initialization doesn't have
    > a CLEANUP_POINT_EXPR in it and wrap both the _ZGVZ3barvEDC1x1y1z1wE
    > and cp_finish_decomp created stuff into a single CLEANUP_POINT_EXPR.
    > That way, perhaps _ZGVZ3barvEDC1x1y1z1wE could be initialized by one 
thread
    > and _ZGVZ3barvE1x by a different, but the temporaries from 
_ZGVZ3barvEDC1x1y1z1wE
    > initialization would be only destructed after the _ZGVZ3barvE1w guard
    > was released by the thread which initialized _ZGVZ3barvEDC1x1y1z1wE.
    
    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).
    
    2025-01-22  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.

Diff:
---
 gcc/cp/decl.cc                      |  19 ++++-
 gcc/testsuite/g++.dg/DRs/dr2867-3.C | 159 ++++++++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/DRs/dr2867-4.C | 108 ++++++++++++++++++++++++
 3 files changed, 285 insertions(+), 1 deletion(-)

diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index af1b89fb8c9b..d6fb76e70345 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -9428,7 +9428,24 @@ cp_finish_decl (tree decl, tree init, bool 
init_const_expr_p,
         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
diff --git a/gcc/testsuite/g++.dg/DRs/dr2867-3.C 
b/gcc/testsuite/g++.dg/DRs/dr2867-3.C
new file mode 100644
index 000000000000..2762fe04b939
--- /dev/null
+++ b/gcc/testsuite/g++.dg/DRs/dr2867-3.C
@@ -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);
+}
diff --git a/gcc/testsuite/g++.dg/DRs/dr2867-4.C 
b/gcc/testsuite/g++.dg/DRs/dr2867-4.C
new file mode 100644
index 000000000000..b7491fecd354
--- /dev/null
+++ b/gcc/testsuite/g++.dg/DRs/dr2867-4.C
@@ -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);
+}

Reply via email to