From patchwork Tue Sep 10 18:29:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 1983457 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=G+5CHFQk; 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 4X3C1f15QCz1y1C for ; Wed, 11 Sep 2024 04:30:29 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 8F9E0385843B for ; Tue, 10 Sep 2024 18:30:27 +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 087773858D39 for ; Tue, 10 Sep 2024 18:30:06 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 087773858D39 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 087773858D39 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=1725993009; cv=none; b=NfuOlGYPdWvGFOT2B4U3/q2O6sxR2nRJqZihIL8+IqiyMoDNTV6EZetdL2wIKyKlvFRz4TjopgtZKn7XQDa+i2znlFZkjI7PEuycYFO1hpAiWSGME5Sc63/30RG2k5MWdasjvVb0qq9V+7qc2dCl526TXlTHpxJkkqCqfq/PNig= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1725993009; c=relaxed/simple; bh=yLF/7KuY3oPIdF5e4Ngp8CyCyAXw+qFFw8kwDYPb6s0=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=HnZr1lDPL9+bdmtTmiwI3FEUM2OsIzwa/6Q58vm1R9iYsc9/HfDLbDcjEtdKG6OmpiaIN5ZQxo36tHGG7ZrOV2AArJ1tyJmR00OeoSjxjLHyk5/S28vs+qJbUY8jEL6eXFj3AY5LZ/ollyI2Q+dWq2OKKy0QJPk4sDjCEKgJ0eo= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1725993005; 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; bh=n89jbvxaGLHM7QeFhkNrTZVUEbhAAYEM3K3Zo9VhoRM=; b=G+5CHFQkGq5MZJu0g1x668XV5sfXWrhr2gGsj6pxDI+esOVJy+pFDA36QaXPSwXegkMr28 M9XZc3YheB+JEm0BWKnhXpU/uqmOupdJdV9tF+JIVqEY+wU1trZrgfULN7ljypZC66zIPq ZfBnQTvlI/7FcYhgb3buJXHIcvb1RX8= 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-686-4d_Y4pMSMoybdQ03eGuDpA-1; Tue, 10 Sep 2024 14:30:04 -0400 X-MC-Unique: 4d_Y4pMSMoybdQ03eGuDpA-1 Received: from mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.40]) (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 5EBEB19560B7 for ; Tue, 10 Sep 2024 18:30:03 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.45.224.29]) by mx-prod-int-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 3374F195605A; Tue, 10 Sep 2024 18:30:01 +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 48AITxmO3053052 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Tue, 10 Sep 2024 20:29:59 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 48AITwR43053051; Tue, 10 Sep 2024 20:29:58 +0200 Date: Tue, 10 Sep 2024 20:29:58 +0200 From: Jakub Jelinek To: Jason Merrill Cc: gcc-patches@gcc.gnu.org Subject: [PATCH] c++: Implement for namespace statics CWG 2867 - Order of initialization for structured bindings [PR115769] Message-ID: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.40 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_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP 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 Hi! The following patch on top of the https://gcc.gnu.org/pipermail/gcc-patches/2024-September/662507.html patch adds CWG 2867 support for namespace locals. Those vars are just pushed into {static,tls}_aggregates chain, then pruned from those lists, separated by priority and finally emitted into the corresponding dynamic initialization functions. The patch adds two flags used on the TREE_LIST nodes in those lists, one marks the structured binding base variable and/or associated ref extended temps, another marks the vars initialized using get methods. The flags are preserved across the pruning, for splitting into by priority all associated decls of a structured binding using tuple* are forced into the same priority as the first one, and finally when actually emitting code, CLEANUP_POINT_EXPRs are disabled in the base initializer(s) and code from the bases and non-bases together is wrapped into a single CLEANUP_POINT_EXPR. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? Note, I haven't touched the module handling; from what I can see, prune_vars_needing_no_initialization is destructive to the {static,tls}_aggregates lists (keeps the list NULL at the end or if there are errors or it contains some DECL_EXTERNAL decls, keeps in there just those, not the actual vars that need dynamic initialization) and the module writing is done only afterwards, so I think it could work reasonably only if header_module_p (). Can namespace scope structured bindings appear in header_module_p () or !header_module_p () modules? How would a testcase using them look like? Especially when structured bindings can't be extern nor templates nor inline there can be just one definition, so the module would need to be included in a single file, no? In any case, the patch shouldn't make the modules case any worse, it just adds TREE_LIST flags which will not be streamed for modules and so if one can use structured bindings in modules, possibly CWG 2867 would be not fixed for those but nothing worse than that. 2024-09-10 Jakub Jelinek PR c++/115769 gcc/cp/ * cp-tree.h (STATIC_INIT_DECOMP_BASE_P): Define. (STATIC_INIT_DECOMP_NONBASE_P): Define. * decl.cc (cp_finish_decl): Mark nodes in {static,tls}_aggregates with * decl2.cc (decomp_handle_one_var, decomp_finalize_var_list): New functions. (emit_partial_init_fini_fn): Use them. (prune_vars_needing_no_initialization): Clear STATIC_INIT_DECOMP_*BASE_P flags if needed. (partition_vars_for_init_fini): Use same priority for consecutive STATIC_INIT_DECOMP_*BASE_P vars and propagate those flags to new TREE_LISTs when possible. Formatting fix. (handle_tls_init): Use decomp_handle_one_var and decomp_finalize_var_list functions. gcc/testsuite/ * g++.dg/DRs/dr2867-5.C: New test. * g++.dg/DRs/dr2867-6.C: New test. * g++.dg/DRs/dr2867-7.C: New test. * g++.dg/DRs/dr2867-8.C: New test. Jakub --- gcc/cp/cp-tree.h.jj 2024-09-07 09:31:20.601484156 +0200 +++ gcc/cp/cp-tree.h 2024-09-09 15:53:44.924112247 +0200 @@ -470,6 +470,7 @@ extern GTY(()) tree cp_global_trees[CPTI BASELINK_FUNCTIONS_MAYBE_INCOMPLETE_P (in BASELINK) BIND_EXPR_VEC_DTOR (in BIND_EXPR) ATOMIC_CONSTR_EXPR_FROM_CONCEPT_P (in ATOMIC_CONSTR) + STATIC_INIT_DECOMP_BASE_P (in the TREE_LIST for {static,tls}_aggregates) 2: IDENTIFIER_KIND_BIT_2 (in IDENTIFIER_NODE) ICS_THIS_FLAG (in _CONV) DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (in VAR_DECL) @@ -489,6 +490,8 @@ extern GTY(()) tree cp_global_trees[CPTI IMPLICIT_CONV_EXPR_BRACED_INIT (in IMPLICIT_CONV_EXPR) PACK_EXPANSION_AUTO_P (in *_PACK_EXPANSION) contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT) + STATIC_INIT_DECOMP_NONBASE_P (in the TREE_LIST + for {static,tls}_aggregates) 3: IMPLICIT_RVALUE_P (in NON_LVALUE_EXPR or STATIC_CAST_EXPR) ICS_BAD_FLAG (in _CONV) FN_TRY_BLOCK_P (in TRY_BLOCK) @@ -5947,6 +5950,21 @@ extern bool defer_mangling_aliases; extern bool flag_noexcept_type; +/* True if this TREE_LIST in {static,tls}_aggregates is a for dynamic + initialization of namespace scope structured binding base or related + extended ref init temps. Temporaries from the initialization of + STATIC_INIT_DECOMP_BASE_P dynamic initializers should be destroyed only + after the last STATIC_INIT_DECOMP_NONBASE_P dynamic initializer following + it. */ +#define STATIC_INIT_DECOMP_BASE_P(NODE) \ + TREE_LANG_FLAG_1 (TREE_LIST_CHECK (NODE)) + +/* True if this TREE_LIST in {static,tls}_aggregates is a for dynamic + initialization of namespace scope structured binding non-base + variable using get. */ +#define STATIC_INIT_DECOMP_NONBASE_P(NODE) \ + TREE_LANG_FLAG_2 (TREE_LIST_CHECK (NODE)) + /* A list of namespace-scope objects which have constructors or destructors which reside in the global scope. The decl is stored in the TREE_VALUE slot and the initializer is stored in the --- gcc/cp/decl.cc.jj 2024-09-09 11:50:07.146394047 +0200 +++ gcc/cp/decl.cc 2024-09-09 17:16:26.459094150 +0200 @@ -8485,6 +8485,7 @@ cp_finish_decl (tree decl, tree init, bo bool var_definition_p = false; tree auto_node; auto_vec extra_cleanups; + tree aggregates1 = NULL_TREE; struct decomp_cleanup { tree decl; cp_decomp *&decomp; @@ -8872,7 +8873,16 @@ cp_finish_decl (tree decl, tree init, bo } if (decomp) - cp_maybe_mangle_decomp (decl, decomp); + { + cp_maybe_mangle_decomp (decl, decomp); + if (TREE_STATIC (decl) && !DECL_FUNCTION_SCOPE_P (decl)) + { + if (CP_DECL_THREAD_LOCAL_P (decl)) + aggregates1 = tls_aggregates; + else + aggregates1 = static_aggregates; + } + } /* If this is a local variable that will need a mangled name, register it now. We must do this before processing the @@ -9210,6 +9220,32 @@ cp_finish_decl (tree decl, tree init, bo if (decomp_init) add_stmt (decomp_init); + if (decomp + && var_definition_p + && TREE_STATIC (decl) + && !DECL_FUNCTION_SCOPE_P (decl)) + { + tree &aggregates3 = (CP_DECL_THREAD_LOCAL_P (decl) + ? tls_aggregates : static_aggregates); + tree aggregates2 = aggregates3; + if (aggregates2 != aggregates1) + { + cp_finish_decomp (decl, decomp); + decomp = NULL; + if (aggregates3 != aggregates2) + { + /* If there are dynamic initializers for the structured + binding base or associated extended ref temps and also + dynamic initializers for the structured binding non-base + vars, mark them. */ + for (tree t = aggregates3; t != aggregates2; t = TREE_CHAIN (t)) + STATIC_INIT_DECOMP_NONBASE_P (t) = 1; + for (tree t = aggregates2; t != aggregates1; t = TREE_CHAIN (t)) + STATIC_INIT_DECOMP_BASE_P (t) = 1; + } + } + } + if (was_readonly) TREE_READONLY (decl) = 1; --- gcc/cp/decl2.cc.jj 2024-09-06 13:43:37.759302079 +0200 +++ gcc/cp/decl2.cc 2024-09-10 13:04:05.475783972 +0200 @@ -4424,6 +4424,55 @@ one_static_initialization_or_destruction DECL_STATIC_FUNCTION_P (current_function_decl) = 0; } +/* Helper function for emit_partial_init_fini_fn and handle_tls_init. + For structured bindings, disable stmts_are_full_exprs_p () + on STATIC_INIT_DECOMP_BASE_P nodes, reenable it on the + first STATIC_INIT_DECOMP_NONBASE_P node and emit all the + STATIC_INIT_DECOMP_BASE_P and STATIC_INIT_DECOMP_NONBASE_P + consecutive nodes in a single STATEMENT_LIST wrapped with + CLEANUP_POINT_EXPR. */ + +static inline tree +decomp_handle_one_var (tree node, tree sl, bool *saw_nonbase, + int save_stmts_are_full_exprs_p) +{ + if (sl && !*saw_nonbase && STATIC_INIT_DECOMP_NONBASE_P (node)) + { + *saw_nonbase = true; + current_stmt_tree ()->stmts_are_full_exprs_p + = save_stmts_are_full_exprs_p; + } + else if (sl && *saw_nonbase && !STATIC_INIT_DECOMP_NONBASE_P (node)) + { + sl = pop_stmt_list (sl); + sl = maybe_cleanup_point_expr_void (sl); + add_stmt (sl); + sl = NULL_TREE; + } + if (sl == NULL_TREE && STATIC_INIT_DECOMP_BASE_P (node)) + { + sl = push_stmt_list (); + *saw_nonbase = false; + current_stmt_tree ()->stmts_are_full_exprs_p = 0; + } + return sl; +} + +/* Similarly helper called when the whole var list is processed. */ + +static inline void +decomp_finalize_var_list (tree sl, int save_stmts_are_full_exprs_p) +{ + if (sl) + { + current_stmt_tree ()->stmts_are_full_exprs_p + = save_stmts_are_full_exprs_p; + sl = pop_stmt_list (sl); + sl = maybe_cleanup_point_expr_void (sl); + add_stmt (sl); + } +} + /* Generate code to do the initialization or destruction of the decls in VARS, a TREE_LIST of VAR_DECL with static storage duration. Whether initialization or destruction is performed is specified by INITP. */ @@ -4453,12 +4502,17 @@ emit_partial_init_fini_fn (bool initp, u finish_if_stmt_cond (target_dev_p, nonhost_if_stmt); } + tree sl = NULL_TREE; + int save_stmts_are_full_exprs_p = stmts_are_full_exprs_p (); + bool saw_nonbase = false; for (tree node = vars; node; node = TREE_CHAIN (node)) { tree decl = TREE_VALUE (node); tree init = TREE_PURPOSE (node); - /* We will emit 'init' twice, and it is modified in-place during - gimplification. Make a copy here. */ + sl = decomp_handle_one_var (node, sl, &saw_nonbase, + save_stmts_are_full_exprs_p); + /* We will emit 'init' twice, and it is modified in-place during + gimplification. Make a copy here. */ if (omp_target) { /* We've already emitted INIT in the host version of the ctor/dtor @@ -4482,6 +4536,7 @@ emit_partial_init_fini_fn (bool initp, u /* Do one initialization or destruction. */ one_static_initialization_or_destruction (initp, decl, init); } + decomp_finalize_var_list (sl, save_stmts_are_full_exprs_p); if (omp_target) { @@ -4510,6 +4565,7 @@ prune_vars_needing_no_initialization (tr { tree *var = vars; tree result = NULL_TREE; + bool clear_nonbase = false; while (*var) { @@ -4517,6 +4573,20 @@ prune_vars_needing_no_initialization (tr tree decl = TREE_VALUE (t); tree init = TREE_PURPOSE (t); + if (STATIC_INIT_DECOMP_BASE_P (t) + && result != NULL_TREE + && STATIC_INIT_DECOMP_NONBASE_P (result)) + clear_nonbase = true; + else if (clear_nonbase && !STATIC_INIT_DECOMP_BASE_P (t)) + { + clear_nonbase = false; + for (tree r = result; r; r = TREE_CHAIN (r)) + if (STATIC_INIT_DECOMP_NONBASE_P (r)) + STATIC_INIT_DECOMP_NONBASE_P (r) = 0; + else + break; + } + /* Deal gracefully with error. */ if (error_operand_p (decl)) { @@ -4544,6 +4614,28 @@ prune_vars_needing_no_initialization (tr continue; } + clear_nonbase = false; + /* 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. */ + if (STATIC_INIT_DECOMP_BASE_P (t) + && !(result != NULL_TREE + && (STATIC_INIT_DECOMP_BASE_P (result) + || STATIC_INIT_DECOMP_NONBASE_P (result)))) + STATIC_INIT_DECOMP_BASE_P (t) = 0; + else if (!STATIC_INIT_DECOMP_BASE_P (t) + && !STATIC_INIT_DECOMP_NONBASE_P (t) + && result != NULL_TREE + && STATIC_INIT_DECOMP_NONBASE_P (result)) + { + for (tree r = result; r; r = TREE_CHAIN (r)) + if (STATIC_INIT_DECOMP_NONBASE_P (r)) + STATIC_INIT_DECOMP_NONBASE_P (r) = 0; + else + break; + } + /* This variable is going to need initialization and/or finalization, so we add it to the list. */ *var = TREE_CHAIN (t); @@ -4560,12 +4652,19 @@ prune_vars_needing_no_initialization (tr void partition_vars_for_init_fini (tree var_list, priority_map_t *(&parts)[4]) { + unsigned priority = 0; + unsigned decomp_state = 0; for (auto node = var_list; node; node = TREE_CHAIN (node)) { tree decl = TREE_VALUE (node); tree init = TREE_PURPOSE (node); bool has_cleanup = !TYPE_HAS_TRIVIAL_DESTRUCTOR (TREE_TYPE (decl)); - unsigned priority = DECL_EFFECTIVE_INIT_PRIORITY (decl); + if (decomp_state == 1 && STATIC_INIT_DECOMP_NONBASE_P (node)) + decomp_state = 2; + else if (decomp_state == 2 && !STATIC_INIT_DECOMP_NONBASE_P (node)) + decomp_state = 0; + if (!decomp_state) + priority = DECL_EFFECTIVE_INIT_PRIORITY (decl); if (init || (flag_use_cxa_atexit && has_cleanup)) { @@ -4574,6 +4673,34 @@ partition_vars_for_init_fini (tree var_l parts[true] = priority_map_t::create_ggc (); auto &slot = parts[true]->get_or_insert (priority); slot = tree_cons (init, decl, slot); + if (init + && STATIC_INIT_DECOMP_BASE_P (node) + && decomp_state == 0) + { + /* If one or more STATIC_INIT_DECOMP_BASE_P with at least + one init is followed by at least one + STATIC_INIT_DECOMP_NONBASE_P with init, mark it in the + resulting chain as well. */ + for (tree n = TREE_CHAIN (node); n; n = TREE_CHAIN (n)) + if (STATIC_INIT_DECOMP_BASE_P (n)) + continue; + else if (STATIC_INIT_DECOMP_NONBASE_P (n)) + { + if (TREE_PURPOSE (n)) + { + decomp_state = 1; + break; + } + else + continue; + } + else + break; + } + if (init && decomp_state == 1) + STATIC_INIT_DECOMP_BASE_P (slot) = 1; + else if (decomp_state == 2) + STATIC_INIT_DECOMP_NONBASE_P (slot) = 1; } if (!flag_use_cxa_atexit && has_cleanup) @@ -4586,7 +4713,7 @@ partition_vars_for_init_fini (tree var_l } if (flag_openmp - && lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl))) + && lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl))) { priority_map_t **omp_parts = parts + 2; @@ -4597,6 +4724,10 @@ partition_vars_for_init_fini (tree var_l omp_parts[true] = priority_map_t::create_ggc (); auto &slot = omp_parts[true]->get_or_insert (priority); slot = tree_cons (init, decl, slot); + if (init && decomp_state == 1) + STATIC_INIT_DECOMP_BASE_P (slot) = 1; + else if (decomp_state == 2) + STATIC_INIT_DECOMP_NONBASE_P (slot) = 1; } if (!flag_use_cxa_atexit && has_cleanup) @@ -4983,10 +5114,15 @@ handle_tls_init (void) finish_expr_stmt (cp_build_modify_expr (loc, guard, NOP_EXPR, boolean_true_node, tf_warning_or_error)); + tree sl = NULL_TREE; + int save_stmts_are_full_exprs_p = stmts_are_full_exprs_p (); + bool saw_nonbase = false; for (; vars; vars = TREE_CHAIN (vars)) { tree var = TREE_VALUE (vars); tree init = TREE_PURPOSE (vars); + sl = decomp_handle_one_var (vars, sl, &saw_nonbase, + save_stmts_are_full_exprs_p); one_static_initialization_or_destruction (/*initp=*/true, var, init); /* Output init aliases even with -fno-extern-tls-init. */ @@ -5001,6 +5137,7 @@ handle_tls_init (void) gcc_assert (alias != NULL); } } + decomp_finalize_var_list (sl, save_stmts_are_full_exprs_p); finish_then_clause (if_stmt); finish_if_stmt (if_stmt); --- gcc/testsuite/g++.dg/DRs/dr2867-5.C.jj 2024-09-09 14:09:22.181185411 +0200 +++ gcc/testsuite/g++.dg/DRs/dr2867-5.C 2024-09-10 10:44:40.859421538 +0200 @@ -0,0 +1,92 @@ +// 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 +bar (int &x, int y) +{ + x = y; + return y; +} + +int +baz (int &x, int y) +{ + assert (x == y); + return y; +} + +struct E { + ~E () { assert (a == 2); } +}; + +E e; +int c1 = bar (c, 1); +const auto &[x, y, z, w] = foo (B {}, B {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } } +int c2 = baz (c, 11); +int d1 = bar (d, 1); +const auto &[s, t, u] = foo (C {}, C {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } } +int d2 = baz (d, 4); +int c3 = bar (c, 1); +auto [x2, y2, z2, w2] = foo (B {}, B {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } } +int c4 = baz (c, 11); +int d3 = bar (d, 1); +auto [s2, t2, u2] = foo (C {}, C {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } } +int d4 = baz (d, 4); + +int +main () +{ + assert (a == 0); +} --- gcc/testsuite/g++.dg/DRs/dr2867-6.C.jj 2024-09-09 14:19:56.455059937 +0200 +++ gcc/testsuite/g++.dg/DRs/dr2867-6.C 2024-09-09 14:56:22.572568526 +0200 @@ -0,0 +1,83 @@ +// 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 +bar (int &x, int y) +{ + x = y; + return y; +} + +int +baz (int &x, int y) +{ + assert (x == y); + return y; +} + +struct E { + ~E () { assert (a == 5); } +}; + +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 {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } } +int c2 = baz (c, 23); + +int +main () +{ + assert (a == 0); +} --- gcc/testsuite/g++.dg/DRs/dr2867-7.C.jj 2024-09-10 12:08:07.770933520 +0200 +++ gcc/testsuite/g++.dg/DRs/dr2867-7.C 2024-09-10 12:19:48.730462845 +0200 @@ -0,0 +1,98 @@ +// CWG2867 - Order of initialization for structured bindings. +// { dg-do run { target c++11 } } +// { dg-options "" } +// { dg-add-options tls } +// { dg-require-effective-target tls_runtime } + +#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 +bar (int &x, int y) +{ + x = y; + return y; +} + +int +baz (int &x, int y) +{ + assert (x == y); + return y; +} + +struct E { + ~E () { assert (a == 2); } +}; + +thread_local E e; +thread_local int c1 = bar (c, 1); +thread_local const auto &[x, y, z, w] = foo (B {}, B {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } } +thread_local int c2 = baz (c, 11); // { dg-warning "structured binding declaration can be 'thread_local' only in" "" { target c++17_down } .-1 } +thread_local int d1 = bar (d, 1); +thread_local const auto &[s, t, u] = foo (C {}, C {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } } +thread_local int d2 = baz (d, 4); // { dg-warning "structured binding declaration can be 'thread_local' only in" "" { target c++17_down } .-1 } +thread_local int c3 = bar (c, 1); +thread_local auto [x2, y2, z2, w2] = foo (B {}, B {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } } +thread_local int c4 = baz (c, 11); // { dg-warning "structured binding declaration can be 'thread_local' only in" "" { target c++17_down } .-1 } +thread_local int d3 = bar (d, 1); +thread_local auto [s2, t2, u2] = foo (C {}, C {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } } +thread_local int d4 = baz (d, 4); // { dg-warning "structured binding declaration can be 'thread_local' only in" "" { target c++17_down } .-1 } + +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/DRs/dr2867-8.C.jj 2024-09-10 12:09:28.773839087 +0200 +++ gcc/testsuite/g++.dg/DRs/dr2867-8.C 2024-09-10 12:34:06.556878510 +0200 @@ -0,0 +1,86 @@ +// CWG2867 - Order of initialization for structured bindings. +// { dg-do run { target c++11 } } +// { dg-options "" } +// { dg-add-options tls } +// { dg-require-effective-target tls_runtime } + +#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 +bar (int &x, int y) +{ + x = y; + return y; +} + +int +baz (int &x, int y) +{ + assert (x == y); + return y; +} + +struct E { + ~E () { assert (a == 5); } +}; + +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 {}); // { dg-warning "structured bindings only available with" "" { target c++14_down } } +thread_local int c2 = baz (c, 23); // { dg-warning "structured binding declaration can be 'thread_local' only in" "" { target c++17_down } .-1 } + +int +main () +{ + volatile int u = c1 + c2; + assert (a == 0); +}