https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115769
--- Comment #9 from GCC Commits <cvs-commit at gcc dot gnu.org> --- The master branch has been updated by Jakub Jelinek <ja...@gcc.gnu.org>: https://gcc.gnu.org/g:964577c31df206d780d5cc7bc07189d44de2719e commit r15-3513-g964577c31df206d780d5cc7bc07189d44de2719e Author: Jakub Jelinek <ja...@redhat.com> Date: Fri Sep 6 13:50:47 2024 +0200 c++: Partially implement CWG 2867 - Order of initialization for structured bindings [PR115769] The following patch partially implements CWG 2867 - Order of initialization for structured bindings. The DR requires that initialization of e is sequenced before r_i and that r_i initialization is sequenced before r_j for j > i, we already do it that way, the former ordering is a necessity so that the get calls are actually emitted on already initialized variable, the rest just because we implemented it that way, by going through the structured binding vars in ascending order and doing their initialization. The hard part not implemented yet is the lifetime extension of the temporaries from the e initialization to after the get calls (if any). Unlike the range-for lifetime extension patch which I've posted recently where IMO we can just ignore lifetime extension of reference bound temporaries because all the temporaries are extended to the same spot, here lifetime extension of reference bound temporaries should last until the end of lifetime of e, while other temporaries only after all the get calls. The patch just attempts to deal with automatic structured bindings for now, I'll post a patch for static locals incrementally and I don't have a patch for namespace scope structured bindings yet, this patch should just keep existing behavior for both static locals and namespace scope structured bindings. What GCC currently emits is a CLEANUP_POINT_EXPR around the e initialization, followed optionally by nested CLEANUP_STMTs for cleanups like the e dtor if any and dtors of lifetime extended temporaries from reference binding; inside of the CLEANUP_STMT CLEANUP_BODY then the initialization of the individual variables for the tuple case, again with optional CLEANUP_STMT if e.g. lifetime extended temporaries from reference binding are needed in those. The following patch drops that first CLEANUP_POINT_EXPR and instead wraps the whole sequence of the e initialization and the individual variable initialization with get calls after it into a single CLEANUP_POINT_EXPR. If there are any CLEANUP_STMTs needed, they are all emitted first, with the CLEANUP_POINT_EXPR for e initialization and the individual variable initialization inside of those, and a guard variable set after different phases in those expressions guarding the corresponding cleanups, so that they aren't invoked until the respective variables are constructed. This is implemented by cp_finish_decl doing cp_finish_decomp on its own when !processing_template_decl (otherwise we often don't cp_finish_decl or process it at a different time from when we want to call cp_finish_decomp) or unless the decl is erroneous (cp_finish_decl has too many early returns for erroneous cases, and for those we can actually call it even multiple times, for the non-erroneous cases non-processing_template_decl cases we need to call it just once). The two testcases try to construct various temporaries and variables and verify the order in which the temporaries and variables are constructed and destructed. 2024-09-06 Jakub Jelinek <ja...@redhat.com> PR c++/115769 * cp-tree.h: Partially implement CWG 2867 - Order of initialization for structured bindings. (cp_finish_decomp): Add TEST_P argument defaulted to false. * decl.cc (initialize_local_var): Add DECOMP argument, if true, don't build cleanup and temporarily override stmts_are_full_exprs_p to 0 rather than 1. Formatting fix. (cp_finish_decl): Invoke cp_finish_decomp for structured bindings here, first with test_p. For automatic structured binding bases if the test cp_finish_decomp returned true wrap the initialization together with what non-test cp_finish_decomp emits with a CLEANUP_POINT_EXPR, and if there are any CLEANUP_STMTs needed, emit them around the whole CLEANUP_POINT_EXPR with guard variables for the cleanups. Call cp_finish_decomp using RAII if not called with decomp != NULL otherwise. (cp_finish_decomp): Add TEST_P argument, change return type from void to bool, if TEST_P is true, return true instead of emitting actual code for the tuple case, otherwise return false. * parser.cc (cp_convert_range_for): Don't call cp_finish_decomp after cp_finish_decl. (cp_parser_decomposition_declaration): Set DECL_DECOMP_BASE before cp_finish_decl call. Don't call cp_finish_decomp after cp_finish_decl. (cp_finish_omp_range_for): Don't call cp_finish_decomp after cp_finish_decl. * pt.cc (tsubst_stmt): Likewise. * g++.dg/DRs/dr2867-1.C: New test. * g++.dg/DRs/dr2867-2.C: New test.