From patchwork Wed Aug 14 16:11:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 1972470 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=RHoM8+fd; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WkYDb69bnz1yZl for ; Thu, 15 Aug 2024 02:12:11 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 4518B3858431 for ; Wed, 14 Aug 2024 16:12:07 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id BB9D63858401 for ; Wed, 14 Aug 2024 16:11:42 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org BB9D63858401 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org BB9D63858401 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1723651905; cv=none; b=dFm3/0PcgZ7j0mu5OYoYQ1tXM8T0TV4ecpdvzZnTsxnLbInhrEzRvR2xrHeadBnV0Hop/1xq0LQ3zOuyLtHqBBy/GTIBLHpj3gvaTj17DF2c7Dofxc/mKuAHLNhn2h7c6JQtF2r2iWBBvP2QyrEWz1+tWYQT/PG2IJIHENcrmkM= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1723651905; c=relaxed/simple; bh=0uEXnpUYgK0G9OH4OtdnyZ+JWRETKHtyFqTFYF6I468=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=Mj6wAFmYIL6aQ0XSqiriMx2yvOa9PRylI8qkDK6Od9Qo4OVCNGeXNEv/BpHIP1o4Wiz8xCrvuEMdmKIRm66cjTrWpT03fjjHmkP8oelpQv6ibAmID3jPgL/NG8c6XnmxQPhkrwM+xarnOd1JgqsfqWQGjVfR+6SJkUhjcQkqg2E= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1723651902; h=from:from:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type:in-reply-to:in-reply-to: references:references; bh=dunqbMMugiACpadgmR1dGVw7AXDpF37vhV+gr5Parxw=; b=RHoM8+fdm8AATzyRZqz49+SOX80IGWTuooFNxL7Q4QIDNV2cltzv31koXLE1gbGtRut+2C EnLvJONOMoM3LlKYX9jEV/URZVsu4sXxrcRKM537yQKwOn4TGF2/Ab9xFyP1JpWr7KAPjD 63GJaME2pkJCk6M9bl669Qkd3DDTCEY= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-558-9VQ36CDFNbyXWdIYQ5oCtw-1; Wed, 14 Aug 2024 12:11:40 -0400 X-MC-Unique: 9VQ36CDFNbyXWdIYQ5oCtw-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 365881918166 for ; Wed, 14 Aug 2024 16:11:39 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.45.224.8]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 7F67B300019A; Wed, 14 Aug 2024 16:11:38 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.17.1/8.17.1) with ESMTPS id 47EGBZAk083857 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Wed, 14 Aug 2024 18:11:35 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 47EGBZLn083856; Wed, 14 Aug 2024 18:11:35 +0200 Date: Wed, 14 Aug 2024 18:11:35 +0200 From: Jakub Jelinek To: Jason Merrill Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] c++: Implement for static locals CWG 2867 - Order of initialization for structured bindings [PR115769] Message-ID: References: MIME-Version: 1.0 In-Reply-To: X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-3.6 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Jakub Jelinek Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org 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). 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. 2024-08-14 Jakub Jelinek 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. Jakub --- gcc/cp/decl.cc.jj 2024-08-13 19:18:42.170052535 +0200 +++ gcc/cp/decl.cc 2024-08-14 10:15:18.021182513 +0200 @@ -9121,7 +9121,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 (need_decomp_init && 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); + sl = pop_stmt_list (sl); + sl = maybe_cleanup_point_expr_void (sl); + add_stmt (sl); + need_decomp_init = false; + } + 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 struct tuple_size; + template struct tuple_element; +} + +int a, c, d, i; + +struct A { + A () { assert (c == 3); ++c; } + ~A () { ++a; } + template int &get () const { assert (c == 5 + I); ++c; return i; } +}; + +template <> struct std::tuple_size { static const int value = 4; }; +template struct std::tuple_element { using type = int; }; +template <> struct std::tuple_size { static const int value = 4; }; +template struct std::tuple_element { 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 &get () const { assert (d == 1 + I); ++d; return i; } +}; + +template <> struct std::tuple_size { static const int value = 3; }; +template struct std::tuple_element { using type = int; }; +template <> struct std::tuple_size { static const int value = 3; }; +template struct std::tuple_element { 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 +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 +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 +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 +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 (); + assert (c == 12); + corge (); + assert (c == 12); + garply <42> (); + assert (c == 12); + freddy (); + 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 struct tuple_size; + template 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 D get (const C & = C{}) const { assert (c == 6 + 4 * I); ++c; return D {}; } +}; + +template <> struct std::tuple_size { static const int value = 4; }; +template struct std::tuple_element { using type = D; }; +template <> struct std::tuple_size { static const int value = 4; }; +template struct std::tuple_element { 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 +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 +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 (); + assert (c == 24); + assert (a == 0); +}