Skip to content
Snippets Groups Projects
Commit 964577c3 authored by Jakub Jelinek's avatar Jakub Jelinek Committed by Jakub Jelinek
Browse files

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  <jakub@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.
parent e8378231
No related branches found
No related tags found
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment