From 650e91566561870f3d1c8d5b92e6613296ee1a8d Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Tue, 24 Sep 2024 20:19:50 +0200 Subject: [PATCH] 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 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) : 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. --- gcc/c-family/c-cppbuiltin.cc | 6 +- gcc/c-family/c-omp.cc | 1 + gcc/c-family/c-opts.cc | 15 ++ gcc/c-family/c.opt | 4 + gcc/cp/call.cc | 6 + gcc/cp/cp-tree.h | 4 +- gcc/cp/decl.cc | 5 + gcc/cp/parser.cc | 57 +++- gcc/cp/pt.cc | 39 ++- gcc/cp/semantics.cc | 54 +++- gcc/doc/invoke.texi | 13 +- gcc/omp-general.cc | 2 + gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C | 4 +- gcc/testsuite/g++.dg/cpp23/range-for1.C | 222 ++++++++++++++++ gcc/testsuite/g++.dg/cpp23/range-for2.C | 231 ++++++++++++++++ gcc/testsuite/g++.dg/cpp23/range-for3.C | 7 + gcc/testsuite/g++.dg/cpp23/range-for4.C | 7 + gcc/testsuite/g++.dg/cpp23/range-for5.C | 8 + gcc/testsuite/g++.dg/cpp23/range-for6.C | 8 + gcc/testsuite/g++.dg/cpp23/range-for7.C | 6 + gcc/testsuite/g++.dg/cpp23/range-for8.C | 6 + gcc/testsuite/g++.dg/cpp26/feat-cxx26.C | 4 +- .../g++.dg/warn/Wdangling-reference4.C | 4 +- libgomp/testsuite/libgomp.c++/range-for-1.C | 250 ++++++++++++++++++ libgomp/testsuite/libgomp.c++/range-for-2.C | 6 + libgomp/testsuite/libgomp.c++/range-for-3.C | 7 + libgomp/testsuite/libgomp.c++/range-for-4.C | 7 + libgomp/testsuite/libgomp.c++/range-for-5.C | 7 + 28 files changed, 960 insertions(+), 30 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/range-for1.C create mode 100644 gcc/testsuite/g++.dg/cpp23/range-for2.C create mode 100644 gcc/testsuite/g++.dg/cpp23/range-for3.C create mode 100644 gcc/testsuite/g++.dg/cpp23/range-for4.C create mode 100644 gcc/testsuite/g++.dg/cpp23/range-for5.C create mode 100644 gcc/testsuite/g++.dg/cpp23/range-for6.C create mode 100644 gcc/testsuite/g++.dg/cpp23/range-for7.C create mode 100644 gcc/testsuite/g++.dg/cpp23/range-for8.C create mode 100644 libgomp/testsuite/libgomp.c++/range-for-1.C create mode 100644 libgomp/testsuite/libgomp.c++/range-for-2.C create mode 100644 libgomp/testsuite/libgomp.c++/range-for-3.C create mode 100644 libgomp/testsuite/libgomp.c++/range-for-4.C create mode 100644 libgomp/testsuite/libgomp.c++/range-for-5.C diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc index 97c3ecb7d16..73a07055a86 100644 --- a/gcc/c-family/c-cppbuiltin.cc +++ b/gcc/c-family/c-cppbuiltin.cc @@ -1034,7 +1034,11 @@ c_cpp_builtins (cpp_reader *pfile) cpp_define (pfile, "__cpp_fold_expressions=201603L"); if (cxx_dialect <= cxx17) cpp_define (pfile, "__cpp_nontype_template_args=201411L"); - cpp_define (pfile, "__cpp_range_based_for=201603L"); + if (!flag_range_for_ext_temps) + cpp_define (pfile, "__cpp_range_based_for=201603L"); + else + /* This is the C++23 or -std=c++17 -frange-for-ext-temps value. */ + cpp_define (pfile, "__cpp_range_based_for=202211L"); if (cxx_dialect <= cxx17) cpp_define (pfile, "__cpp_constexpr=201603L"); cpp_define (pfile, "__cpp_if_constexpr=201606L"); diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc index b5ce1466e5d..620a3c1353a 100644 --- a/gcc/c-family/c-omp.cc +++ b/gcc/c-family/c-omp.cc @@ -1617,6 +1617,7 @@ c_find_nested_loop_xform_r (tree *tp, int *walk_subtrees, void *) *walk_subtrees = 1; break; case TRY_FINALLY_EXPR: + case CLEANUP_POINT_EXPR: *walk_subtrees = 1; break; default: diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc index 69dd5281020..86163ddb4ed 100644 --- a/gcc/c-family/c-opts.cc +++ b/gcc/c-family/c-opts.cc @@ -1160,6 +1160,21 @@ c_common_post_options (const char **pfilename) if (cxx_dialect >= cxx20) flag_concepts = 1; + /* Enable lifetime extension of range based for temporaries for C++23. + Diagnose -std=c++23 -fno-range-for-ext-temps. */ + if (cxx_dialect >= cxx23) + { + if (OPTION_SET_P (flag_range_for_ext_temps) + && !flag_range_for_ext_temps) + error ("%<-fno-range-for-ext-temps%> is incompatible with C++23"); + flag_range_for_ext_temps = 1; + } + /* Otherwise default to enabled in GNU modes but allow user to override. */ + else if (cxx_dialect >= cxx11 + && !flag_iso + && !OPTION_SET_P (flag_range_for_ext_temps)) + flag_range_for_ext_temps = 1; + /* -fimmediate-escalation has no effect when immediate functions are not supported. */ if (flag_immediate_escalation && cxx_dialect < cxx20) diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 98a35f043c7..b5983093e24 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -2209,6 +2209,10 @@ fprintf-return-value C ObjC C++ ObjC++ LTO Optimization Var(flag_printf_return_value) Init(1) Treat known sprintf return values as constants. +frange-for-ext-temps +C++ ObjC++ Var(flag_range_for_ext_temps) +Enable lifetime extension of range based for temporaries. + freplace-objc-classes ObjC ObjC++ LTO Var(flag_replace_objc_classes) Used in Fix-and-Continue mode to indicate that object files may be swapped in at runtime. diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 70783baac24..309ab01d12d 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -14564,6 +14564,12 @@ extend_ref_init_temps (tree decl, tree init, vec **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 4809576b654..7c438eca16d 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 2190ede745b..6a7ba416cf8 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 35c266659e4..83ae38a33ab 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 e826206be16..a3a697dd126 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 8219d6410b8..a061704e1a2 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++) + 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 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])); - } + 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)); diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 1f9f3386bf9..bdbbea53666 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -214,7 +214,7 @@ in the following sections. @xref{C++ Dialect Options,,Options Controlling C++ Dialect}. @gccoptlist{-fabi-version=@var{n} -fno-access-control -faligned-new=@var{n} -fargs-in-order=@var{n} -fchar8_t -fcheck-new --fconstexpr-depth=@var{n} -fconstexpr-cache-depth=@var{n} +-fconcepts -fconstexpr-depth=@var{n} -fconstexpr-cache-depth=@var{n} -fconstexpr-loop-limit=@var{n} -fconstexpr-ops-limit=@var{n} -fno-elide-constructors -fno-enforce-eh-specs @@ -233,7 +233,7 @@ in the following sections. -fnew-ttp-matching -fno-nonansi-builtins -fnothrow-opt -fno-operator-names -fno-optional-diags --fno-pretty-templates +-fno-pretty-templates -frange-for-ext-temps -fno-rtti -fsized-deallocation -ftemplate-backtrace-limit=@var{n} -ftemplate-depth=@var{n} @@ -3614,6 +3614,15 @@ the default template arguments for that template. If either of these behaviors make it harder to understand the error message rather than easier, you can use @option{-fno-pretty-templates} to disable them. +@opindex frange-for-ext-temps +@item -frange-for-ext-temps +Enable lifetime extension of C++ range based for temporaries. +With @option{-std=c++23} and above this is part of the language standard, +so lifetime of the temporaries is extended until the end of the loop +regardless of this option. This option allows enabling that behavior also +in earlier versions of the standard and is enabled by default in the +GNU dialects, from @option{-std=gnu++11} until @option{-std=gnu++20}. + @opindex fno-rtti @opindex frtti @item -fno-rtti diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc index 9713e684e83..f4c5f577047 100644 --- a/gcc/omp-general.cc +++ b/gcc/omp-general.cc @@ -972,6 +972,7 @@ find_combined_omp_for (tree *tp, int *walk_subtrees, void *data) *walk_subtrees = 1; break; case TRY_FINALLY_EXPR: + case CLEANUP_POINT_EXPR: pdata[0] = tp; *walk_subtrees = 1; break; @@ -4226,6 +4227,7 @@ find_nested_loop_xform (tree *tp, int *walk_subtrees, void *data) *walk_subtrees = 1; break; case TRY_FINALLY_EXPR: + case CLEANUP_POINT_EXPR: pdata[0] = tp; *walk_subtrees = 1; break; diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C index a1ddc81cefd..4033552b2eb 100644 --- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C +++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C @@ -43,8 +43,8 @@ #ifndef __cpp_range_based_for # error "__cpp_range_based_for" -#elif __cpp_range_based_for != 201603 -# error "__cpp_range_based_for != 201603" +#elif __cpp_range_based_for != 202211 +# error "__cpp_range_based_for != 202211" #endif #ifndef __cpp_decltype diff --git a/gcc/testsuite/g++.dg/cpp23/range-for1.C b/gcc/testsuite/g++.dg/cpp23/range-for1.C new file mode 100644 index 00000000000..2aa8a5e9332 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for1.C @@ -0,0 +1,222 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target c++11 } } + +#ifndef RANGE_FOR_EXT_TEMPS +#define RANGE_FOR_EXT_TEMPS (__cpp_range_based_for >= 202211L) +#else +#if __cplusplus >= 201703L +#if RANGE_FOR_EXT_TEMPS +static_assert (__cpp_range_based_for >= 202211L, ""); +#else +static_assert (__cpp_range_based_for >= 201603L + && __cpp_range_based_for < 202211L, ""); +#endif +#else +static_assert (__cpp_range_based_for >= 200907L + && __cpp_range_based_for < 201603L, ""); +#endif +#endif + +extern "C" void abort (); +void check (bool); + +struct S +{ + S () { ++s; } + S (const S &) { ++s; } + ~S () { check (true); --s; } + static int s; +}; + +int S::s = -1; +S sv; + +struct T +{ + T (const S &, const S &) { ++t; } + T (const T &) { ++t; } + ~T () { check (false); --t; } + static int t; +}; + +int T::t = -1; +T tv (sv, sv); +int a[4]; +int c; + +void +check (bool is_s) +{ + if (c) + { + if (is_s) + { + if (T::t != (c == 1)) + abort (); + } + else + { + if (S::s != (c == 1 ? 0 : 2)) + abort (); + } + } +} + +template +int * +begin (const T &) +{ + return &a[0]; +} + +template +int * +end (const T &) +{ + return &a[4]; +} + +const S & +foo (const S &) +{ + return sv; +} + +const T & +foo (const T &) +{ + return tv; +} + +void +bar () +{ + if (S::s != 0) + abort (); + for (auto x : S ()) + { + if (S::s != 1) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : foo (S ())) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + if (T::t != 0) + abort (); + c = 1 + RANGE_FOR_EXT_TEMPS; + for (auto x : T (S (), S ())) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS || T::t != 1) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + c = 2; + for (auto x : foo (T (S (), S ()))) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS + || T::t != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + c = 0; +} + +template +void +baz () +{ + if (S::s != 0) + abort (); + for (auto x : S ()) + { + if (S::s != 1) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : foo (S ())) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + if (T::t != 0) + abort (); + c = 1 + RANGE_FOR_EXT_TEMPS; + for (auto x : T (S (), S ())) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS || T::t != 1) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + c = 2; + for (auto x : foo (T (S (), S ()))) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS + || T::t != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + c = 0; +} + +template +void +qux () +{ + if (S::s != 0) + abort (); + for (auto x : S ()) + { + if (S::s != 1) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : foo (S ())) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + if (T::t != 0) + abort (); + c = 1 + RANGE_FOR_EXT_TEMPS; + for (auto x : T (S (), S ())) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS || T::t != 1) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + c = 2; + for (auto x : foo (T (S (), S ()))) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS + || T::t != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + c = 0; +} + +int +main () +{ + bar (); + baz <0> (); + qux (); +} diff --git a/gcc/testsuite/g++.dg/cpp23/range-for2.C b/gcc/testsuite/g++.dg/cpp23/range-for2.C new file mode 100644 index 00000000000..e40e6d32a27 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for2.C @@ -0,0 +1,231 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target c++11 } } + +#ifndef RANGE_FOR_EXT_TEMPS +#define RANGE_FOR_EXT_TEMPS (__cpp_range_based_for >= 202211L) +#endif + +extern "C" void abort (); + +int a[4]; + +template +int * +begin (const T &) +{ + return &a[0]; +} + +template +int * +end (const T &) +{ + return &a[4]; +} + +struct S +{ + S () { ++s; } + S (const S &) { ++s; } + ~S () { --s; } + static int s; +}; + +int S::s; + +template +struct U +{ + T t; + U () { ++u; } + U (const U &) { ++u; } + ~U () { --u; } + const int *begin () const { return ::begin (t); } + const int *end () const { return ::end (t); } + U &foo () { return *this; } + U bar () { return U (); } + static int u; +}; + +template +int U::u; + +template +U +foo () +{ + return U {}; +} + +template +T +fred (const T &, const T & = T{}, const T & = T{}) +{ + return T {}; +} + +void +bar () +{ + int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + if (S::s != 0) + abort (); + for (auto x : S (), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : (void) S (), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : static_cast (S ()), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : foo ().foo ().bar ().foo ().bar ().foo ().bar ()) + { + if (S::s != 1 + 3 * RANGE_FOR_EXT_TEMPS) + abort (); + if (U::u != S::s) + abort (); + } + if (S::s != 0 || U::u != 0) + abort (); + for (auto x : fred (S {})) + { + if (S::s != 1 + 3 * RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : fred (fred (S {}, S {}))) + { + if (S::s != 1 + 6 * RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); +} + +template +void +baz () +{ + int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + if (S::s != 0) + abort (); + for (auto x : S (), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : (void) S (), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : static_cast (S ()), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : foo ().foo ().bar ().foo ().bar ().foo ().bar ()) + { + if (S::s != 1 + 3 * RANGE_FOR_EXT_TEMPS) + abort (); + if (U::u != S::s) + abort (); + } + if (S::s != 0 || U::u != 0) + abort (); + for (auto x : fred (S {})) + { + if (S::s != 1 + 3 * RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : fred (fred (S {}, S {}))) + { + if (S::s != 1 + 6 * RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); +} + +template +void +qux () +{ + int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + if (S::s != 0) + abort (); + for (auto x : S (), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : (void) S (), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : static_cast (S ()), a) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : foo ().foo ().bar ().foo ().bar ().foo ().bar ()) + { + if (S::s != 1 + 3 * RANGE_FOR_EXT_TEMPS) + abort (); + if (U::u != S::s) + abort (); + } + if (S::s != 0 || U::u != 0) + abort (); + for (auto x : fred (S {})) + { + if (S::s != 1 + 3 * RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + for (auto x : fred (fred (S {}, S {}))) + { + if (S::s != 1 + 6 * RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); +} + +int +main () +{ + bar (); + baz <0> (); + qux (); +} diff --git a/gcc/testsuite/g++.dg/cpp23/range-for3.C b/gcc/testsuite/g++.dg/cpp23/range-for3.C new file mode 100644 index 00000000000..301e25886ec --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for3.C @@ -0,0 +1,7 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target c++11 } } +// Verify -frange-for-ext-temps is set by default in -std=gnu++* modes. +// { dg-options "" } + +#define RANGE_FOR_EXT_TEMPS 1 +#include "range-for1.C" diff --git a/gcc/testsuite/g++.dg/cpp23/range-for4.C b/gcc/testsuite/g++.dg/cpp23/range-for4.C new file mode 100644 index 00000000000..f8c380d32c7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for4.C @@ -0,0 +1,7 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target c++11 } } +// Verify -frange-for-ext-temps is set by default in -std=gnu++* modes. +// { dg-options "" } + +#define RANGE_FOR_EXT_TEMPS 1 +#include "range-for2.C" diff --git a/gcc/testsuite/g++.dg/cpp23/range-for5.C b/gcc/testsuite/g++.dg/cpp23/range-for5.C new file mode 100644 index 00000000000..c2cd1248bf6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for5.C @@ -0,0 +1,8 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target { c++11 && c++20_down } } } +// { dg-options "-fno-range-for-ext-temps" } + +#if __cplusplus <= 202002L +#define RANGE_FOR_EXT_TEMPS 0 +#endif +#include "range-for1.C" diff --git a/gcc/testsuite/g++.dg/cpp23/range-for6.C b/gcc/testsuite/g++.dg/cpp23/range-for6.C new file mode 100644 index 00000000000..8bf8656e5ef --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for6.C @@ -0,0 +1,8 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target { c++11 && c++20_down } } } +// { dg-options "-fno-range-for-ext-temps" } + +#if __cplusplus <= 202002L +#define RANGE_FOR_EXT_TEMPS 0 +#endif +#include "range-for2.C" diff --git a/gcc/testsuite/g++.dg/cpp23/range-for7.C b/gcc/testsuite/g++.dg/cpp23/range-for7.C new file mode 100644 index 00000000000..99d9bd85bc3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for7.C @@ -0,0 +1,6 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target c++17_only } } +// { dg-options "-std=c++17 -frange-for-ext-temps" } + +#define RANGE_FOR_EXT_TEMPS 1 +#include "range-for1.C" diff --git a/gcc/testsuite/g++.dg/cpp23/range-for8.C b/gcc/testsuite/g++.dg/cpp23/range-for8.C new file mode 100644 index 00000000000..3b2efbc7e41 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/range-for8.C @@ -0,0 +1,6 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run { target c++11_only } } +// { dg-options "-std=c++11 -frange-for-ext-temps" } + +#define RANGE_FOR_EXT_TEMPS 1 +#include "range-for2.C" diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C index 52c89e498fb..c387a7dfe60 100644 --- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C +++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C @@ -43,8 +43,8 @@ #ifndef __cpp_range_based_for # error "__cpp_range_based_for" -#elif __cpp_range_based_for != 201603 -# error "__cpp_range_based_for != 201603" +#elif __cpp_range_based_for != 202211 +# error "__cpp_range_based_for != 202211" #endif #ifndef __cpp_decltype diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference4.C b/gcc/testsuite/g++.dg/warn/Wdangling-reference4.C index 0343bcf226b..ada70dd683a 100644 --- a/gcc/testsuite/g++.dg/warn/Wdangling-reference4.C +++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference4.C @@ -1,5 +1,5 @@ // { dg-do compile { target c++17 } } -// { dg-options "-Wdangling-reference" } +// { dg-additional-options "-Wdangling-reference" } // { dg-skip-if "requires hosted libstdc++ for string" { ! hostedlib } } // Check that we warn here even without -Wsystem-headers. @@ -11,5 +11,5 @@ auto f() -> std::optional; void g () { - for (char c : f().value()) { (void) c; } // { dg-warning "dangling reference" } + for (char c : f().value()) { (void) c; } // { dg-warning "dangling reference" "" { target c++20_down } } } diff --git a/libgomp/testsuite/libgomp.c++/range-for-1.C b/libgomp/testsuite/libgomp.c++/range-for-1.C new file mode 100644 index 00000000000..c92f9c4e145 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/range-for-1.C @@ -0,0 +1,250 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run } +// { dg-additional-options "-std=c++17" } +// { dg-require-effective-target tls_runtime } + +#ifndef RANGE_FOR_EXT_TEMPS +#define RANGE_FOR_EXT_TEMPS (__cpp_range_based_for >= 202211L) +#endif + +extern "C" void abort (); +void check (bool); + +struct S +{ + S () { ++s; } + S (const S &) { ++s; } + ~S () { check (true); --s; } + [[omp::decl (threadprivate)]] static int s; +}; +int S::s; +S sv; +struct T +{ + T (const S &, const S &) { ++t; } + T (const T &) { ++t; } + ~T () { check (false); --t; } + [[omp::decl (threadprivate)]] static int t; +}; +int T::t; +T tv (sv, sv); +int a[4]; +int c; + +void +check (bool is_s) +{ + if (c) + { + if (is_s) + { + if (T::t != (c == 1)) + abort (); + } + else + { + if (S::s != (c == 1 ? 0 : 2)) + abort (); + } + } +} + +template +int * +begin (const T &) +{ + return &a[0]; +} + +template +int * +end (const T &) +{ + return &a[4]; +} + +const S & +foo (const S &) +{ + return sv; +} + +const T & +foo (const T &) +{ + return tv; +} + +void +bar () +{ + #pragma omp parallel num_threads (4) + { + if (S::s != 0) + abort (); + #pragma omp for + for (auto x : S ()) + { + if (S::s != 1) + abort (); + } + if (S::s != 0) + abort (); + #pragma omp for + for (auto x : foo (S ())) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + if (T::t != 0) + abort (); + } + c = 1 + RANGE_FOR_EXT_TEMPS; + #pragma omp parallel num_threads (4) + { + #pragma omp for + for (auto x : T (S (), S ())) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS || T::t != 1) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + } + c = 2; + #pragma omp parallel num_threads (4) + { + #pragma omp for + for (auto x : foo (T (S (), S ()))) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS + || T::t != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + } + c = 0; +} + +template +void +baz () +{ + #pragma omp parallel num_threads (4) + { + if (S::s != 0) + abort (); + #pragma omp for + for (auto x : S ()) + { + if (S::s != 1) + abort (); + } + if (S::s != 0) + abort (); + #pragma omp for + for (auto x : foo (S ())) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + if (T::t != 0) + abort (); + } + c = 1 + RANGE_FOR_EXT_TEMPS; + #pragma omp parallel num_threads (4) + { + #pragma omp for + for (auto x : T (S (), S ())) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS || T::t != 1) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + } + c = 2; + #pragma omp parallel num_threads (4) + { + #pragma omp for + for (auto x : foo (T (S (), S ()))) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS + || T::t != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + } + c = 0; +} + +template +void +qux () +{ + #pragma omp parallel num_threads (4) + { + if (S::s != 0) + abort (); + #pragma omp for + for (auto x : S ()) + { + if (S::s != 1) + abort (); + } + if (S::s != 0) + abort (); + #pragma omp for + for (auto x : foo (S ())) + { + if (S::s != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0) + abort (); + if (T::t != 0) + abort (); + } + c = 1 + RANGE_FOR_EXT_TEMPS; + #pragma omp parallel num_threads (4) + { + #pragma omp for + for (auto x : T (S (), S ())) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS || T::t != 1) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + } + c = 2; + #pragma omp parallel num_threads (4) + { + #pragma omp for + for (auto x : foo (T (S (), S ()))) + { + if (S::s != 2 * RANGE_FOR_EXT_TEMPS + || T::t != RANGE_FOR_EXT_TEMPS) + abort (); + } + if (S::s != 0 || T::t != 0) + abort (); + } + c = 0; +} + +int +main () +{ + S::s--; + T::t--; + bar (); + baz <0> (); + qux (); +} diff --git a/libgomp/testsuite/libgomp.c++/range-for-2.C b/libgomp/testsuite/libgomp.c++/range-for-2.C new file mode 100644 index 00000000000..8c25140cdd5 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/range-for-2.C @@ -0,0 +1,6 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run } +// { dg-additional-options "-std=c++23" } +// { dg-require-effective-target tls_runtime } + +#include "range-for-1.C" diff --git a/libgomp/testsuite/libgomp.c++/range-for-3.C b/libgomp/testsuite/libgomp.c++/range-for-3.C new file mode 100644 index 00000000000..919fe85b374 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/range-for-3.C @@ -0,0 +1,7 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run } +// { dg-additional-options "-std=c++17 -frange-for-ext-temps" } +// { dg-require-effective-target tls_runtime } + +#define RANGE_FOR_EXT_TEMPS 1 +#include "range-for-1.C" diff --git a/libgomp/testsuite/libgomp.c++/range-for-4.C b/libgomp/testsuite/libgomp.c++/range-for-4.C new file mode 100644 index 00000000000..3c10e7349af --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/range-for-4.C @@ -0,0 +1,7 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run } +// { dg-additional-options "-std=gnu++17" } +// { dg-require-effective-target tls_runtime } + +#define RANGE_FOR_EXT_TEMPS 1 +#include "range-for-1.C" diff --git a/libgomp/testsuite/libgomp.c++/range-for-5.C b/libgomp/testsuite/libgomp.c++/range-for-5.C new file mode 100644 index 00000000000..d8c84c32669 --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/range-for-5.C @@ -0,0 +1,7 @@ +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop +// { dg-do run } +// { dg-additional-options "-std=gnu++17 -fno-range-for-ext-temps" } +// { dg-require-effective-target tls_runtime } + +#define RANGE_FOR_EXT_TEMPS 0 +#include "range-for-1.C"