aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2024-09-24 20:19:50 +0200
committerJakub Jelinek <jakub@gcc.gnu.org>2024-09-24 20:24:57 +0200
commit650e91566561870f3d1c8d5b92e6613296ee1a8d (patch)
treee5e1060cfa73ceb13d6cb590ef441b00838c31c9 /gcc/cp
parentd9cafa0c4f0a81304d9b95a78ccc8e9003c6d7a3 (diff)
downloadgcc-650e91566561870f3d1c8d5b92e6613296ee1a8d.zip
gcc-650e91566561870f3d1c8d5b92e6613296ee1a8d.tar.gz
gcc-650e91566561870f3d1c8d5b92e6613296ee1a8d.tar.bz2
c++: Implement C++23 P2718R0 - Wording for P2644R1 Fix for Range-based for Loop [PR107637]
The following patch implements the C++23 P2718R0 paper - Wording for P2644R1 Fix for Range-based for Loop. The patch introduces a new option, -f{,no-}range-for-ext-temps so that user can control the behavior even in older C++ versions. The option is on by default in C++23 and later (-fno-range-for-ext-temps is an error in that case) and in the -std=gnu++11 ... -std=gnu++20 modes (one can use -fno-range-for-ext-temps to request previous behavior in that case), and is not enabled by default in -std=c++11 ... -std=c++20 modes but one can explicitly enable it with -frange-for-ext-temps. As all the temporaries from __for_range initialization should have life extended until the end of __for_range scope, this patch disables (for -frange-for-ext-temps and if !processing_template_decl) CLEANUP_POINT_EXPR wrapping of the __for_range declaration, also disables -Wdangling-reference warning as well as the rest of extend_ref_init_temps (we know the __for_range temporary is not TREE_STATIC and as all the temporaries from the initializer will be life extended, we shouldn't try to handle temporaries referenced by references any differently) and adds an extra push_stmt_list/pop_stmt_list before cp_finish_decl of __for_range and after end of the for body and wraps all that into CLEANUP_POINT_EXPR. I had to repeat that also for OpenMP range loops because those are handled differently. 2024-09-24 Jakub Jelinek <jakub@redhat.com> PR c++/107637 gcc/ * omp-general.cc (find_combined_omp_for, find_nested_loop_xform): Handle CLEANUP_POINT_EXPR like TRY_FINALLY_EXPR. * doc/invoke.texi (frange-for-ext-temps): Document. Add -fconcepts to the C++ option list. gcc/c-family/ * c.opt (frange-for-ext-temps): New option. * c-opts.cc (c_common_post_options): Set flag_range_for_ext_temps for C++23 or later or for C++11 or later in !flag_iso mode if the option wasn't set by user. * c-cppbuiltin.cc (c_cpp_builtins): Change __cpp_range_based_for value for flag_range_for_ext_temps from 201603L to 202212L in C++17 or later. * c-omp.cc (c_find_nested_loop_xform_r): Handle CLEANUP_POINT_EXPR like TRY_FINALLY_EXPR. gcc/cp/ * cp-tree.h: Implement C++23 P2718R0 - Wording for P2644R1 Fix for Range-based for Loop. (cp_convert_omp_range_for): Add bool tmpl_p argument. (find_range_for_decls): Declare. * parser.cc (cp_convert_range_for): For flag_range_for_ext_temps call push_stmt_list () before cp_finish_decl for range_temp and save it temporarily to FOR_INIT_STMT. (cp_convert_omp_range_for): Add tmpl_p argument. If set, remember DECL_NAME of range_temp and for cp_finish_decl call restore it before clearing it again, if unset, don't adjust DECL_NAME of range_temp at all. (cp_parser_omp_loop_nest): For flag_range_for_ext_temps range for add CLEANUP_POINT_EXPR around sl. Call find_range_for_decls and adjust DECL_NAMEs for range fors if not processing_template_decl. Adjust cp_convert_omp_range_for caller. Remove superfluous backslash at the end of line. * decl.cc (initialize_local_var): For flag_range_for_ext_temps temporarily clear stmts_are_full_exprs_p rather than set for for_range__identifier decls. * call.cc (extend_ref_init_temps): For flag_range_for_ext_temps return init early for for_range__identifier decls. * semantics.cc (find_range_for_decls): New function. (finish_for_stmt): Use it. For flag_range_for_ext_temps if cp_convert_range_for set FOR_INIT_STMT, pop_stmt_list it and wrap into CLEANUP_POINT_EXPR. * pt.cc (tsubst_omp_for_iterator): Adjust tsubst_omp_for_iterator caller. (tsubst_stmt) <case OMP_FOR>: For flag_range_for_ext_temps if there are any range fors in the loop nest, add push_stmt_list starting before the initializations, pop_stmt_list it after the body and wrap into CLEANUP_POINT_EXPR. Change DECL_NAME of range for temps from NULL to for_range_identifier. gcc/testsuite/ * g++.dg/cpp23/range-for1.C: New test. * g++.dg/cpp23/range-for2.C: New test. * g++.dg/cpp23/range-for3.C: New test. * g++.dg/cpp23/range-for4.C: New test. * g++.dg/cpp23/range-for5.C: New test. * g++.dg/cpp23/range-for6.C: New test. * g++.dg/cpp23/range-for7.C: New test. * g++.dg/cpp23/range-for8.C: New test. * g++.dg/cpp23/feat-cxx2b.C (__cpp_range_based_for): Check for 202212L rather than 201603L. * g++.dg/cpp26/feat-cxx26.C (__cpp_range_based_for): Likewise. * g++.dg/warn/Wdangling-reference4.C: Don't expect warning for C++23 or newer. Use dg-additional-options rather than dg-options. libgomp/ * testsuite/libgomp.c++/range-for-1.C: New test. * testsuite/libgomp.c++/range-for-2.C: New test. * testsuite/libgomp.c++/range-for-3.C: New test. * testsuite/libgomp.c++/range-for-4.C: New test. * testsuite/libgomp.c++/range-for-5.C: New test.
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/call.cc6
-rw-r--r--gcc/cp/cp-tree.h4
-rw-r--r--gcc/cp/decl.cc5
-rw-r--r--gcc/cp/parser.cc57
-rw-r--r--gcc/cp/pt.cc39
-rw-r--r--gcc/cp/semantics.cc56
6 files changed, 145 insertions, 22 deletions
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 70783ba..309ab01 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -14564,6 +14564,12 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups,
if (processing_template_decl)
return init;
+ /* P2718R0 - ignore temporaries in C++23 for-range-initializer, those
+ have all extended lifetime. */
+ if (DECL_NAME (decl) == for_range__identifier
+ && flag_range_for_ext_temps)
+ return init;
+
maybe_warn_dangling_reference (decl, init);
if (TYPE_REF_P (type))
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 4809576..7c438ec 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7474,7 +7474,8 @@ extern bool maybe_clone_body (tree);
extern tree cp_convert_range_for (tree, tree, tree, cp_decomp *, bool,
tree, bool);
extern void cp_convert_omp_range_for (tree &, tree &, tree &,
- tree &, tree &, tree &, tree &, tree &);
+ tree &, tree &, tree &, tree &, tree &,
+ bool);
extern void cp_finish_omp_range_for (tree, tree);
extern bool cp_maybe_parse_omp_decl (tree, tree);
extern bool parsing_nsdmi (void);
@@ -7809,6 +7810,7 @@ extern tree begin_for_stmt (tree, tree);
extern void finish_init_stmt (tree);
extern void finish_for_cond (tree, tree, bool, tree, bool);
extern void finish_for_expr (tree, tree);
+extern void find_range_for_decls (tree[3]);
extern void finish_for_stmt (tree);
extern tree begin_range_for_stmt (tree, tree);
extern void finish_range_for_decl (tree, tree, tree);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2190ede..6a7ba41 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -8157,6 +8157,11 @@ initialize_local_var (tree decl, tree init, bool decomp)
code emitted by cp_finish_decomp. */
if (decomp)
current_stmt_tree ()->stmts_are_full_exprs_p = 0;
+ /* P2718R0 - avoid CLEANUP_POINT_EXPR for range-for-initializer,
+ temporaries from there should have lifetime extended. */
+ else if (DECL_NAME (decl) == for_range__identifier
+ && flag_range_for_ext_temps)
+ current_stmt_tree ()->stmts_are_full_exprs_p = 0;
else
current_stmt_tree ()->stmts_are_full_exprs_p = 1;
finish_expr_stmt (init);
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 35c2666..83ae38a 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -14480,6 +14480,15 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr,
{
range_temp = build_range_temp (range_expr);
pushdecl (range_temp);
+ if (flag_range_for_ext_temps)
+ {
+ /* P2718R0 - put the range_temp declaration and everything
+ until end of the range for body into an extra STATEMENT_LIST
+ which will have CLEANUP_POINT_EXPR around it, so that all
+ temporaries are destroyed at the end of it. */
+ gcc_assert (FOR_INIT_STMT (statement) == NULL_TREE);
+ FOR_INIT_STMT (statement) = push_stmt_list ();
+ }
cp_finish_decl (range_temp, range_expr,
/*is_constant_init*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING);
@@ -44629,7 +44638,8 @@ cp_parser_omp_for_loop_init (cp_parser *parser,
void
cp_convert_omp_range_for (tree &this_pre_body, tree &sl,
tree &decl, tree &orig_decl, tree &init,
- tree &orig_init, tree &cond, tree &incr)
+ tree &orig_init, tree &cond, tree &incr,
+ bool tmpl_p)
{
tree begin, end, range_temp_decl = NULL_TREE;
tree iter_type, begin_expr, end_expr;
@@ -44687,11 +44697,29 @@ cp_convert_omp_range_for (tree &this_pre_body, tree &sl,
else
{
range_temp = build_range_temp (init);
- DECL_NAME (range_temp) = NULL_TREE;
+ tree name = DECL_NAME (range_temp);
+ /* Temporarily clear DECL_NAME of the __for_range temporary.
+ While it contains a space at the end and outside of templates
+ it works even without doing this, when cp_convert_omp_range_for
+ is called from tsubst_omp_for_iterator, all the associated loops
+ are in a single scope and so loop nests with 2 or more range
+ based for loops would error. */
+ if (tmpl_p)
+ DECL_NAME (range_temp) = NULL_TREE;
pushdecl (range_temp);
+ /* Restore the name back. This is needed for cp_finish_decl
+ lifetime extension of temporaries, and can be helpful for user
+ during debugging after the DECL_NAME is changed to __for_range
+ without space at the end. */
+ if (tmpl_p)
+ DECL_NAME (range_temp) = name;
cp_finish_decl (range_temp, init,
/*is_constant_init*/false, NULL_TREE,
LOOKUP_ONLYCONVERTING);
+ /* And clear the name again. pop_scope requires that the name
+ used during pushdecl didn't change. */
+ if (tmpl_p)
+ DECL_NAME (range_temp) = NULL_TREE;
range_temp_decl = range_temp;
range_temp = convert_from_reference (range_temp);
}
@@ -44791,7 +44819,7 @@ cp_convert_omp_range_for (tree &this_pre_body, tree &sl,
the whole loop nest. The remaining elements are decls of derived
decomposition variables that are bound inside the loop body. This
structure is further mangled by finish_omp_for into the form required
- for the OMP_FOR_ORIG_DECLS field of the OMP_FOR tree node. */\
+ for the OMP_FOR_ORIG_DECLS field of the OMP_FOR tree node. */
unsigned decomp_cnt = decomp ? decomp->count : 0;
tree v = make_tree_vec (decomp_cnt + 3);
TREE_VEC_ELT (v, 0) = range_temp_decl;
@@ -45360,7 +45388,7 @@ cp_parser_omp_loop_nest (cp_parser *parser, bool *if_p)
cp_convert_omp_range_for (this_pre_body, sl, decl,
orig_decl, init, orig_init,
- cond, incr);
+ cond, incr, false);
if (omp_for_parse_state->ordered_cl)
error_at (OMP_CLAUSE_LOCATION (omp_for_parse_state->ordered_cl),
@@ -45623,11 +45651,30 @@ cp_parser_omp_loop_nest (cp_parser *parser, bool *if_p)
/* Pop and remember the init block. */
if (sl)
- add_stmt (pop_stmt_list (sl));
+ {
+ sl = pop_stmt_list (sl);
+ /* P2718R0 - Add CLEANUP_POINT_EXPR so that temporaries in
+ for-range-initializer whose lifetime is extended are destructed
+ here. */
+ if (flag_range_for_ext_temps
+ && is_range_for
+ && !processing_template_decl)
+ sl = maybe_cleanup_point_expr_void (sl);
+ add_stmt (sl);
+ }
+ tree range_for_decl[3] = { NULL_TREE, NULL_TREE, NULL_TREE };
+ if (is_range_for && !processing_template_decl)
+ find_range_for_decls (range_for_decl);
finish_compound_stmt (init_scope);
init_block = pop_stmt_list (init_block);
omp_for_parse_state->init_blockv[depth] = init_block;
+ if (is_range_for && !processing_template_decl)
+ for (int i = 0; i < 3; i++)
+ if (range_for_decl[i])
+ DECL_NAME (range_for_decl[i])
+ = cp_global_trees[CPTI_FOR_RANGE_IDENTIFIER + i];
+
/* Return the init placeholder rather than the remembered init block.
Again, this is just a unique cookie that will be used to reassemble
code pieces when the entire omp for statement has been parsed. */
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index e826206..a3a697d 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -18127,7 +18127,7 @@ tsubst_omp_for_iterator (tree t, int i, tree declv, tree &orig_declv,
tree orig_decl = NULL_TREE;
tree init_sl = NULL_TREE;
cp_convert_omp_range_for (this_pre_body, init_sl, decl, orig_decl, init,
- orig_init, cond, incr);
+ orig_init, cond, incr, true);
if (orig_decl)
{
if (orig_declv == NULL_TREE)
@@ -19156,6 +19156,18 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
RECUR (OMP_FOR_PRE_BODY (t));
pre_body = pop_stmt_list (pre_body);
+ tree sl = NULL_TREE;
+ if (flag_range_for_ext_temps
+ && OMP_FOR_INIT (t) != NULL_TREE
+ && !processing_template_decl)
+ for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (t)); i++)
+ if (TREE_VEC_ELT (OMP_FOR_INIT (t), i)
+ && TREE_VEC_ELT (OMP_FOR_COND (t), i) == global_namespace)
+ {
+ sl = push_stmt_list ();
+ break;
+ }
+
if (OMP_FOR_INIT (t) != NULL_TREE)
for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (t)); i++)
{
@@ -19211,9 +19223,34 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
add_stmt (t);
}
+ if (sl)
+ {
+ /* P2718R0 - Add CLEANUP_POINT_EXPR so that temporaries in
+ for-range-initializer whose lifetime is extended are destructed
+ here. */
+ sl = pop_stmt_list (sl);
+ sl = maybe_cleanup_point_expr_void (sl);
+ add_stmt (sl);
+ }
+
add_stmt (finish_omp_for_block (finish_omp_structured_block (stmt),
t));
pop_omp_privatization_clauses (r);
+
+ if (any_range_for && !processing_template_decl && t)
+ for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (t)); i++)
+ if (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (t), i)
+ && TREE_CODE (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (t),
+ i)) == TREE_LIST)
+ {
+ tree v = TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (t), i);
+ if (TREE_CHAIN (v) == NULL_TREE
+ || TREE_CODE (TREE_CHAIN (v)) != TREE_VEC)
+ continue;
+ v = TREE_VEC_ELT (TREE_CHAIN (v), 0);
+ gcc_assert (VAR_P (v) && DECL_NAME (v) == NULL_TREE);
+ DECL_NAME (v) = for_range_identifier;
+ }
}
break;
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 8219d641..a061704 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -1637,6 +1637,33 @@ finish_for_expr (tree expr, tree for_stmt)
FOR_EXPR (for_stmt) = expr;
}
+/* During parsing of the body, range for uses "__for_{range,begin,end} "
+ decl names to make those unaccessible by code in the body.
+ Find those decls and store into RANGE_FOR_DECL array, so that they
+ can be changed later to ones with underscore instead of space, so that
+ it can be inspected in the debugger. */
+
+void
+find_range_for_decls (tree range_for_decl[3])
+{
+ gcc_assert (CPTI_FOR_BEGIN__IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 1
+ && CPTI_FOR_END__IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 2
+ && CPTI_FOR_RANGE_IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 3
+ && CPTI_FOR_BEGIN_IDENTIFIER == CPTI_FOR_BEGIN__IDENTIFIER + 3
+ && CPTI_FOR_END_IDENTIFIER == CPTI_FOR_END__IDENTIFIER + 3);
+ for (int i = 0; i < 3; i++)
+ {
+ tree id = cp_global_trees[CPTI_FOR_RANGE__IDENTIFIER + i];
+ if (IDENTIFIER_BINDING (id)
+ && IDENTIFIER_BINDING (id)->scope == current_binding_level)
+ {
+ range_for_decl[i] = IDENTIFIER_BINDING (id)->value;
+ gcc_assert (VAR_P (range_for_decl[i])
+ && DECL_ARTIFICIAL (range_for_decl[i]));
+ }
+ }
+}
+
/* Finish the body of a for-statement, which may be given by
FOR_STMT. The increment-EXPR for the loop must be
provided.
@@ -1670,21 +1697,20 @@ finish_for_stmt (tree for_stmt)
Change it to ones with underscore instead of space, so that it can
be inspected in the debugger. */
tree range_for_decl[3] = { NULL_TREE, NULL_TREE, NULL_TREE };
- gcc_assert (CPTI_FOR_BEGIN__IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 1
- && CPTI_FOR_END__IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 2
- && CPTI_FOR_RANGE_IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 3
- && CPTI_FOR_BEGIN_IDENTIFIER == CPTI_FOR_BEGIN__IDENTIFIER + 3
- && CPTI_FOR_END_IDENTIFIER == CPTI_FOR_END__IDENTIFIER + 3);
- for (int i = 0; i < 3; i++)
- {
- tree id = cp_global_trees[CPTI_FOR_RANGE__IDENTIFIER + i];
- if (IDENTIFIER_BINDING (id)
- && IDENTIFIER_BINDING (id)->scope == current_binding_level)
- {
- range_for_decl[i] = IDENTIFIER_BINDING (id)->value;
- gcc_assert (VAR_P (range_for_decl[i])
- && DECL_ARTIFICIAL (range_for_decl[i]));
- }
+ find_range_for_decls (range_for_decl);
+
+ /* P2718R0 - Add CLEANUP_POINT_EXPR so that temporaries in
+ for-range-initializer whose lifetime is extended are destructed
+ here. */
+ if (flag_range_for_ext_temps
+ && range_for_decl[0]
+ && FOR_INIT_STMT (for_stmt))
+ {
+ tree stmt = pop_stmt_list (FOR_INIT_STMT (for_stmt));
+ FOR_INIT_STMT (for_stmt) = NULL_TREE;
+ stmt = build_stmt (EXPR_LOCATION (for_stmt), EXPR_STMT, stmt);
+ stmt = maybe_cleanup_point_expr_void (stmt);
+ add_stmt (stmt);
}
add_stmt (do_poplevel (scope));