diff options
author | Jakub Jelinek <jakub@redhat.com> | 2024-09-24 20:19:50 +0200 |
---|---|---|
committer | Jakub Jelinek <jakub@gcc.gnu.org> | 2024-09-24 20:24:57 +0200 |
commit | 650e91566561870f3d1c8d5b92e6613296ee1a8d (patch) | |
tree | e5e1060cfa73ceb13d6cb590ef441b00838c31c9 /gcc/cp | |
parent | d9cafa0c4f0a81304d9b95a78ccc8e9003c6d7a3 (diff) | |
download | gcc-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.cc | 6 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 4 | ||||
-rw-r--r-- | gcc/cp/decl.cc | 5 | ||||
-rw-r--r-- | gcc/cp/parser.cc | 57 | ||||
-rw-r--r-- | gcc/cp/pt.cc | 39 | ||||
-rw-r--r-- | gcc/cp/semantics.cc | 56 |
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)); |