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.
This commit is contained in:
parent
d9cafa0c4f
commit
650e915665
28 changed files with 960 additions and 30 deletions
|
@ -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");
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
39
gcc/cp/pt.cc
39
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;
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
222
gcc/testsuite/g++.dg/cpp23/range-for1.C
Normal file
222
gcc/testsuite/g++.dg/cpp23/range-for1.C
Normal file
|
@ -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 <typename T>
|
||||
int *
|
||||
begin (const T &)
|
||||
{
|
||||
return &a[0];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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 <int N>
|
||||
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 <typename S, typename T>
|
||||
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 <S, T> ();
|
||||
}
|
231
gcc/testsuite/g++.dg/cpp23/range-for2.C
Normal file
231
gcc/testsuite/g++.dg/cpp23/range-for2.C
Normal file
|
@ -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 <typename T>
|
||||
int *
|
||||
begin (const T &)
|
||||
{
|
||||
return &a[0];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int *
|
||||
end (const T &)
|
||||
{
|
||||
return &a[4];
|
||||
}
|
||||
|
||||
struct S
|
||||
{
|
||||
S () { ++s; }
|
||||
S (const S &) { ++s; }
|
||||
~S () { --s; }
|
||||
static int s;
|
||||
};
|
||||
|
||||
int S::s;
|
||||
|
||||
template <typename T>
|
||||
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 <typename T>
|
||||
int U<T>::u;
|
||||
|
||||
template <typename T>
|
||||
U<T>
|
||||
foo ()
|
||||
{
|
||||
return U<T> {};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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<void> (S ()), a)
|
||||
{
|
||||
if (S::s != RANGE_FOR_EXT_TEMPS)
|
||||
abort ();
|
||||
}
|
||||
if (S::s != 0)
|
||||
abort ();
|
||||
for (auto x : foo <S> ().foo ().bar ().foo ().bar ().foo ().bar ())
|
||||
{
|
||||
if (S::s != 1 + 3 * RANGE_FOR_EXT_TEMPS)
|
||||
abort ();
|
||||
if (U<S>::u != S::s)
|
||||
abort ();
|
||||
}
|
||||
if (S::s != 0 || U<S>::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 <int N>
|
||||
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<void> (S ()), a)
|
||||
{
|
||||
if (S::s != RANGE_FOR_EXT_TEMPS)
|
||||
abort ();
|
||||
}
|
||||
if (S::s != 0)
|
||||
abort ();
|
||||
for (auto x : foo <S> ().foo ().bar ().foo ().bar ().foo ().bar ())
|
||||
{
|
||||
if (S::s != 1 + 3 * RANGE_FOR_EXT_TEMPS)
|
||||
abort ();
|
||||
if (U<S>::u != S::s)
|
||||
abort ();
|
||||
}
|
||||
if (S::s != 0 || U<S>::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 <typename S>
|
||||
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<void> (S ()), a)
|
||||
{
|
||||
if (S::s != RANGE_FOR_EXT_TEMPS)
|
||||
abort ();
|
||||
}
|
||||
if (S::s != 0)
|
||||
abort ();
|
||||
for (auto x : foo <S> ().foo ().bar ().foo ().bar ().foo ().bar ())
|
||||
{
|
||||
if (S::s != 1 + 3 * RANGE_FOR_EXT_TEMPS)
|
||||
abort ();
|
||||
if (U<S>::u != S::s)
|
||||
abort ();
|
||||
}
|
||||
if (S::s != 0 || U<S>::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 <S> ();
|
||||
}
|
7
gcc/testsuite/g++.dg/cpp23/range-for3.C
Normal file
7
gcc/testsuite/g++.dg/cpp23/range-for3.C
Normal file
|
@ -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"
|
7
gcc/testsuite/g++.dg/cpp23/range-for4.C
Normal file
7
gcc/testsuite/g++.dg/cpp23/range-for4.C
Normal file
|
@ -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"
|
8
gcc/testsuite/g++.dg/cpp23/range-for5.C
Normal file
8
gcc/testsuite/g++.dg/cpp23/range-for5.C
Normal file
|
@ -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"
|
8
gcc/testsuite/g++.dg/cpp23/range-for6.C
Normal file
8
gcc/testsuite/g++.dg/cpp23/range-for6.C
Normal file
|
@ -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"
|
6
gcc/testsuite/g++.dg/cpp23/range-for7.C
Normal file
6
gcc/testsuite/g++.dg/cpp23/range-for7.C
Normal file
|
@ -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"
|
6
gcc/testsuite/g++.dg/cpp23/range-for8.C
Normal file
6
gcc/testsuite/g++.dg/cpp23/range-for8.C
Normal file
|
@ -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"
|
|
@ -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
|
||||
|
|
|
@ -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<std::string>;
|
|||
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 } }
|
||||
}
|
||||
|
|
250
libgomp/testsuite/libgomp.c++/range-for-1.C
Normal file
250
libgomp/testsuite/libgomp.c++/range-for-1.C
Normal file
|
@ -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 <typename T>
|
||||
int *
|
||||
begin (const T &)
|
||||
{
|
||||
return &a[0];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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 <int N>
|
||||
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 <typename S, typename T>
|
||||
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 <S, T> ();
|
||||
}
|
6
libgomp/testsuite/libgomp.c++/range-for-2.C
Normal file
6
libgomp/testsuite/libgomp.c++/range-for-2.C
Normal file
|
@ -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"
|
7
libgomp/testsuite/libgomp.c++/range-for-3.C
Normal file
7
libgomp/testsuite/libgomp.c++/range-for-3.C
Normal file
|
@ -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"
|
7
libgomp/testsuite/libgomp.c++/range-for-4.C
Normal file
7
libgomp/testsuite/libgomp.c++/range-for-4.C
Normal file
|
@ -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"
|
7
libgomp/testsuite/libgomp.c++/range-for-5.C
Normal file
7
libgomp/testsuite/libgomp.c++/range-for-5.C
Normal file
|
@ -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"
|
Loading…
Add table
Reference in a new issue