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:
Jakub Jelinek 2024-09-24 20:19:50 +02:00 committed by Jakub Jelinek
parent d9cafa0c4f
commit 650e915665
28 changed files with 960 additions and 30 deletions

View file

@ -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");

View file

@ -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:

View file

@ -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)

View file

@ -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.

View file

@ -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))

View file

@ -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);

View file

@ -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);

View file

@ -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. */

View file

@ -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;

View file

@ -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));

View file

@ -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

View file

@ -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;

View file

@ -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

View 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> ();
}

View 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> ();
}

View 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"

View 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"

View 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"

View 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"

View 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"

View 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"

View file

@ -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

View file

@ -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 } }
}

View 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> ();
}

View 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"

View 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"

View 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"

View 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"