@@ -972,6 +972,7 @@ find_combined_omp_for (tree *tp, int *wa
*walk_subtrees = 1;
break;
case TRY_FINALLY_EXPR:
+ case CLEANUP_POINT_EXPR:
pdata[0] = tp;
*walk_subtrees = 1;
break;
@@ -4164,6 +4165,7 @@ find_nested_loop_xform (tree *tp, int *w
*walk_subtrees = 1;
break;
case TRY_FINALLY_EXPR:
+ case CLEANUP_POINT_EXPR:
pdata[0] = tp;
*walk_subtrees = 1;
break;
@@ -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
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
@@ -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.
@@ -1160,6 +1160,16 @@ c_common_post_options (const char **pfil
if (cxx_dialect >= cxx20)
flag_concepts = 1;
+ /* Enable lifetime extension of range based for temporaries for C++23
+ regardless of command line setting. */
+ if (cxx_dialect >= cxx23)
+ 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)
@@ -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, in
*walk_subtrees = 1;
break;
case TRY_FINALLY_EXPR:
+ case CLEANUP_POINT_EXPR:
*walk_subtrees = 1;
break;
default:
@@ -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);
@@ -14480,6 +14480,15 @@ cp_convert_range_for (tree statement, tr
{
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 *
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
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
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 *pars
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 *pars
/* 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. */
@@ -8157,6 +8157,11 @@ initialize_local_var (tree decl, tree in
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);
@@ -14564,6 +14564,12 @@ extend_ref_init_temps (tree decl, tree i
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))
@@ -1637,6 +1637,31 @@ finish_for_expr (tree expr, tree for_stm
FOR_EXPR (for_stmt) = expr;
}
+void
+find_range_for_decls (tree range_for_decl[3])
+{
+ /* During parsing of the body, range for uses "__for_{range,begin,end} "
+ decl names to make those unaccessible by code in the body.
+ Change it to ones with underscore instead of space, so that it can
+ be inspected in the debugger. */
+ 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 +1695,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));
@@ -18127,7 +18127,7 @@ tsubst_omp_for_iterator (tree t, int i,
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_f
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_f
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;
@@ -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> ();
+}
@@ -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> ();
+}
@@ -0,0 +1,6 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+#define RANGE_FOR_EXT_TEMPS 1
+#include "range-for1.C"
@@ -0,0 +1,6 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+#define RANGE_FOR_EXT_TEMPS 1
+#include "range-for2.C"
@@ -0,0 +1,8 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run { target c++11 } }
+// { dg-options "-fno-range-for-ext-temps" }
+
+#if __cplusplus <= 202002L
+#define RANGE_FOR_EXT_TEMPS 0
+#endif
+#include "range-for1.C"
@@ -0,0 +1,8 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run { target c++11 } }
+// { dg-options "-fno-range-for-ext-temps" }
+
+#if __cplusplus <= 202002L
+#define RANGE_FOR_EXT_TEMPS 0
+#endif
+#include "range-for2.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"
@@ -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
@@ -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 } }
}
@@ -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> ();
+}
@@ -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"
@@ -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"
@@ -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"
@@ -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"