diff mbox series

c++, v2: Implement C++23 P2718R0 - Wording for P2644R1 Fix for Range-based for Loop [PR107637]

Message ID ZvLugnxnpcbyeDVd@tucnak
State New
Headers show
Series c++, v2: Implement C++23 P2718R0 - Wording for P2644R1 Fix for Range-based for Loop [PR107637] | expand

Commit Message

Jakub Jelinek Sept. 24, 2024, 4:53 p.m. UTC
On Mon, Sep 23, 2024 at 03:46:36PM -0400, Jason Merrill wrote:
> > -frange-based-for-ext-temps
> > or do you have better suggestion?
> 
> I'd probably drop "based", "range-for" seems enough.
> 
> > Shall we allow also disabling it in C++23 or later modes, or override
> > user choice unconditionally for C++23+ and only allow users to
> > enable/disable it in C++11-C++20?
> 
> Hmm, I think the latter.
> 
> > What about the __cpp_range_based_for predefined macro?
> > Shall it be defined to the C++23 202211L value if the switch is on?
> > While that could be done in theory for C++17 and later code, for C++11/14
> > __cpp_range_based_for is 200907L and doesn't include the C++17
> > 201603L step.  Or keep the macro only for C++23 and later?
> 
> I think update the macro for 17 and later.

Ok.

Here is a new patch.

> > > > @@ -44600,11 +44609,14 @@ cp_convert_omp_range_for (tree &this_pre
> > > >          else
> > > >    	{
> > > >    	  range_temp = build_range_temp (init);
> > > > +	  tree name = DECL_NAME (range_temp);
> > > >    	  DECL_NAME (range_temp) = NULL_TREE;
> > > >    	  pushdecl (range_temp);
> > > > +	  DECL_NAME (range_temp) = name;
> > > >    	  cp_finish_decl (range_temp, init,
> > > >    			  /*is_constant_init*/false, NULL_TREE,
> > > >    			  LOOKUP_ONLYCONVERTING);
> > > > +	  DECL_NAME (range_temp) = NULL_TREE;
> > > 
> > > This messing with the name needs a rationale.  What wants it to be null?
> > 
> > I'll add comments.  The first = NULL_TREE; is needed so that pushdecl
> > doesn't register the temporary for name lookup, the = name now is so that
> > cp_finish_decl recognizes the temporary as range based for temporary
> > for the lifetime extension, and the last one is just to preserve previous
> > behavior, not have it visible in debug info etc.
> 
> But cp_convert_range_for doesn't ever set the name to NULL_TREE, why should
> the OMP variant be different?
> 
> Having it visible to name lookup in the debugger seems beneficial. Having it
> visible to the code seems less useful, but not important to prevent.

So, in the end it works fine even for the OpenMP case when not inside of a
template, all I had to add is the renaming of the symbol at the end after
pop_scope from "__for_range " to "__for_range" etc.
It doesn't work unfortunately during instantiation, we only create a single
scope in that case for the whole loop nest rather than one for each loop in
it and changing that isn't easy.  With the "__for_range " name in, if there
are 2+ range based for loops in the OpenMP loop nest (collapsed or ordered),
one gets then errors about defining it multiple times.
I'll try to fix that up at incrementally later, for now I just went with
a new flag to the function, so that it does the DECL_NAME dances only when
called from the instantiation (and confirmed actually all 3 spots are
needed, clearing before pushdecl, resetting back before cp_finish_decl and
clearing after cp_finish_decl, the last one so that pop_scope doesn't ICE
on seeing the name change).

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

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.



	Jakub

Comments

Jason Merrill Sept. 24, 2024, 5:34 p.m. UTC | #1
On 9/24/24 12:53 PM, Jakub Jelinek wrote:
> On Mon, Sep 23, 2024 at 03:46:36PM -0400, Jason Merrill wrote:
>>> -frange-based-for-ext-temps
>>> or do you have better suggestion?
>>
>> I'd probably drop "based", "range-for" seems enough.
>>
>>> Shall we allow also disabling it in C++23 or later modes, or override
>>> user choice unconditionally for C++23+ and only allow users to
>>> enable/disable it in C++11-C++20?
>>
>> Hmm, I think the latter.
>>
>>> What about the __cpp_range_based_for predefined macro?
>>> Shall it be defined to the C++23 202211L value if the switch is on?
>>> While that could be done in theory for C++17 and later code, for C++11/14
>>> __cpp_range_based_for is 200907L and doesn't include the C++17
>>> 201603L step.  Or keep the macro only for C++23 and later?
>>
>> I think update the macro for 17 and later.
> 
> Ok.
> 
> Here is a new patch.
> 
>>>>> @@ -44600,11 +44609,14 @@ cp_convert_omp_range_for (tree &this_pre
>>>>>           else
>>>>>     	{
>>>>>     	  range_temp = build_range_temp (init);
>>>>> +	  tree name = DECL_NAME (range_temp);
>>>>>     	  DECL_NAME (range_temp) = NULL_TREE;
>>>>>     	  pushdecl (range_temp);
>>>>> +	  DECL_NAME (range_temp) = name;
>>>>>     	  cp_finish_decl (range_temp, init,
>>>>>     			  /*is_constant_init*/false, NULL_TREE,
>>>>>     			  LOOKUP_ONLYCONVERTING);
>>>>> +	  DECL_NAME (range_temp) = NULL_TREE;
>>>>
>>>> This messing with the name needs a rationale.  What wants it to be null?
>>>
>>> I'll add comments.  The first = NULL_TREE; is needed so that pushdecl
>>> doesn't register the temporary for name lookup, the = name now is so that
>>> cp_finish_decl recognizes the temporary as range based for temporary
>>> for the lifetime extension, and the last one is just to preserve previous
>>> behavior, not have it visible in debug info etc.
>>
>> But cp_convert_range_for doesn't ever set the name to NULL_TREE, why should
>> the OMP variant be different?
>>
>> Having it visible to name lookup in the debugger seems beneficial. Having it
>> visible to the code seems less useful, but not important to prevent.
> 
> So, in the end it works fine even for the OpenMP case when not inside of a
> template, all I had to add is the renaming of the symbol at the end after
> pop_scope from "__for_range " to "__for_range" etc.
> It doesn't work unfortunately during instantiation, we only create a single
> scope in that case for the whole loop nest rather than one for each loop in
> it and changing that isn't easy.  With the "__for_range " name in, if there
> are 2+ range based for loops in the OpenMP loop nest (collapsed or ordered),
> one gets then errors about defining it multiple times.
> I'll try to fix that up at incrementally later, for now I just went with
> a new flag to the function, so that it does the DECL_NAME dances only when
> called from the instantiation (and confirmed actually all 3 spots are
> needed, clearing before pushdecl, resetting back before cp_finish_decl and
> clearing after cp_finish_decl, the last one so that pop_scope doesn't ICE
> on seeing the name change).

Don't worry too much about fixing it up if it's complicated.

> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
> 
> 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.
> 
> --- gcc/omp-general.cc.jj	2024-09-24 11:31:48.775621337 +0200
> +++ gcc/omp-general.cc	2024-09-24 11:37:02.761332388 +0200
> @@ -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;
> --- gcc/doc/invoke.texi.jj	2024-09-24 11:31:48.695622430 +0200
> +++ gcc/doc/invoke.texi	2024-09-24 12:09:08.908039208 +0200
> @@ -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
> --- gcc/c-family/c.opt.jj	2024-09-24 11:31:37.380776989 +0200
> +++ gcc/c-family/c.opt	2024-09-24 11:49:23.856220260 +0200
> @@ -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.
> --- gcc/c-family/c-opts.cc.jj	2024-09-24 11:31:37.344777481 +0200
> +++ gcc/c-family/c-opts.cc	2024-09-24 13:36:47.135322261 +0200
> @@ -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;

Let's also give an error for trying to disable it in C++23+.

> +++ gcc/cp/semantics.cc	2024-09-24 12:20:35.083694190 +0200
> @@ -1637,6 +1637,31 @@ finish_for_expr (tree expr, tree for_stm
>     FOR_EXPR (for_stmt) = expr;
>   }

Missing function comment, maybe just use the one below?

> +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/testsuite/g++.dg/cpp23/range-for3.C.jj	2024-09-24 13:46:42.151189765 +0200
> +++ gcc/testsuite/g++.dg/cpp23/range-for3.C	2024-09-24 13:47:26.571582778 +0200
> @@ -0,0 +1,6 @@
> +// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
> +// { dg-do run { target c++11 } }
> +// { dg-options "" }

Please add a comment to this and range-for4 explaining that this is to 
get the fix enabled in GNU modes.

OK with those changes.

Jason
Jakub Jelinek Sept. 24, 2024, 6:28 p.m. UTC | #2
On Tue, Sep 24, 2024 at 01:34:44PM -0400, Jason Merrill wrote:
> Let's also give an error for trying to disable it in C++23+.
> Missing function comment, maybe just use the one below?
> Please add a comment to this and range-for4 explaining that this is to get
> the fix enabled in GNU modes.
> 
> OK with those changes.

Done, committed now, thanks for the review.

I've also committed the following tweak for the status page:

diff --git a/htdocs/projects/cxx-status.html b/htdocs/projects/cxx-status.html
index d986fc79..76f6ef6d 100644
--- a/htdocs/projects/cxx-status.html
+++ b/htdocs/projects/cxx-status.html
@@ -576,7 +576,7 @@
     <tr>
       <td> Wording for P2644R1 Fix for Range-based for Loop </td>
       <td> <a href="https://wg21.link/p2718">P2718R0</a></td>
-      <td class="unsupported"> <a href="https://gcc.gnu.org/PR107637">No</a></td>
+      <td class="supported"> <a href="../gcc-15/changes.html#cxx">15</a></td>
       <td> __cpp_range_based_for >= 202211L </td>
     </tr>
     <!--


	Jakub
diff mbox series

Patch

--- gcc/omp-general.cc.jj	2024-09-24 11:31:48.775621337 +0200
+++ gcc/omp-general.cc	2024-09-24 11:37:02.761332388 +0200
@@ -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;
--- gcc/doc/invoke.texi.jj	2024-09-24 11:31:48.695622430 +0200
+++ gcc/doc/invoke.texi	2024-09-24 12:09:08.908039208 +0200
@@ -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
--- gcc/c-family/c.opt.jj	2024-09-24 11:31:37.380776989 +0200
+++ gcc/c-family/c.opt	2024-09-24 11:49:23.856220260 +0200
@@ -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.
--- gcc/c-family/c-opts.cc.jj	2024-09-24 11:31:37.344777481 +0200
+++ gcc/c-family/c-opts.cc	2024-09-24 13:36:47.135322261 +0200
@@ -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)
--- gcc/c-family/c-cppbuiltin.cc.jj	2024-09-24 11:31:37.312777918 +0200
+++ gcc/c-family/c-cppbuiltin.cc	2024-09-24 11:56:29.641414229 +0200
@@ -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");
--- gcc/c-family/c-omp.cc.jj	2024-09-24 11:31:37.343777494 +0200
+++ gcc/c-family/c-omp.cc	2024-09-24 11:37:02.762332374 +0200
@@ -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:
--- gcc/cp/cp-tree.h.jj	2024-09-21 12:28:13.310942697 +0200
+++ gcc/cp/cp-tree.h	2024-09-24 14:41:32.378371615 +0200
@@ -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);
--- gcc/cp/parser.cc.jj	2024-09-24 11:31:48.684622580 +0200
+++ gcc/cp/parser.cc	2024-09-24 14:41:01.505787999 +0200
@@ -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.  */
--- gcc/cp/decl.cc.jj	2024-09-24 11:37:02.772332238 +0200
+++ gcc/cp/decl.cc	2024-09-24 11:38:20.535270020 +0200
@@ -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);
--- gcc/cp/call.cc.jj	2024-09-24 11:31:37.386776907 +0200
+++ gcc/cp/call.cc	2024-09-24 11:37:02.774332211 +0200
@@ -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))
--- gcc/cp/semantics.cc.jj	2024-09-24 11:31:37.516775131 +0200
+++ gcc/cp/semantics.cc	2024-09-24 12:20:35.083694190 +0200
@@ -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));
--- gcc/cp/pt.cc.jj	2024-09-24 11:31:37.495775418 +0200
+++ gcc/cp/pt.cc	2024-09-24 14:42:19.277739068 +0200
@@ -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;
 
--- gcc/testsuite/g++.dg/cpp23/range-for1.C.jj	2024-09-24 11:37:02.779332142 +0200
+++ gcc/testsuite/g++.dg/cpp23/range-for1.C	2024-09-24 13:45:38.218063385 +0200
@@ -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> ();
+}
--- gcc/testsuite/g++.dg/cpp23/range-for2.C.jj	2024-09-24 11:37:02.779332142 +0200
+++ gcc/testsuite/g++.dg/cpp23/range-for2.C	2024-09-24 13:46:31.802331179 +0200
@@ -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> ();
+}
--- gcc/testsuite/g++.dg/cpp23/range-for3.C.jj	2024-09-24 13:46:42.151189765 +0200
+++ gcc/testsuite/g++.dg/cpp23/range-for3.C	2024-09-24 13:47:26.571582778 +0200
@@ -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"
--- gcc/testsuite/g++.dg/cpp23/range-for4.C.jj	2024-09-24 13:47:35.949454632 +0200
+++ gcc/testsuite/g++.dg/cpp23/range-for4.C	2024-09-24 13:47:47.078302564 +0200
@@ -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"
--- gcc/testsuite/g++.dg/cpp23/range-for5.C.jj	2024-09-24 13:52:10.709700152 +0200
+++ gcc/testsuite/g++.dg/cpp23/range-for5.C	2024-09-24 13:54:45.031591404 +0200
@@ -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"
--- gcc/testsuite/g++.dg/cpp23/range-for6.C.jj	2024-09-24 13:54:53.146480511 +0200
+++ gcc/testsuite/g++.dg/cpp23/range-for6.C	2024-09-24 13:54:59.713390778 +0200
@@ -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"
--- gcc/testsuite/g++.dg/cpp23/range-for7.C.jj	2024-09-24 13:55:14.980182159 +0200
+++ gcc/testsuite/g++.dg/cpp23/range-for7.C	2024-09-24 13:55:36.945882011 +0200
@@ -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"
--- gcc/testsuite/g++.dg/cpp23/range-for8.C.jj	2024-09-24 13:55:44.815774472 +0200
+++ gcc/testsuite/g++.dg/cpp23/range-for8.C	2024-09-24 13:55:56.186619103 +0200
@@ -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"
--- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj	2024-09-24 11:31:37.555774598 +0200
+++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C	2024-09-24 11:37:02.779332142 +0200
@@ -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
--- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj	2024-09-24 11:31:37.555774598 +0200
+++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C	2024-09-24 11:37:02.780332129 +0200
@@ -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
--- gcc/testsuite/g++.dg/warn/Wdangling-reference4.C.jj	2024-09-24 11:31:37.556774585 +0200
+++ gcc/testsuite/g++.dg/warn/Wdangling-reference4.C	2024-09-24 11:37:02.780332129 +0200
@@ -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 } }
 }
--- libgomp/testsuite/libgomp.c++/range-for-1.C.jj	2024-09-24 11:37:02.780332129 +0200
+++ libgomp/testsuite/libgomp.c++/range-for-1.C	2024-09-24 14:00:52.214575429 +0200
@@ -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> ();
+}
--- libgomp/testsuite/libgomp.c++/range-for-2.C.jj	2024-09-24 11:37:02.780332129 +0200
+++ libgomp/testsuite/libgomp.c++/range-for-2.C	2024-09-24 11:37:02.780332129 +0200
@@ -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"
--- libgomp/testsuite/libgomp.c++/range-for-3.C.jj	2024-09-24 14:03:08.554714647 +0200
+++ libgomp/testsuite/libgomp.c++/range-for-3.C	2024-09-24 14:02:02.282619132 +0200
@@ -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"
--- libgomp/testsuite/libgomp.c++/range-for-4.C.jj	2024-09-24 14:03:15.428620831 +0200
+++ libgomp/testsuite/libgomp.c++/range-for-4.C	2024-09-24 14:02:22.983336609 +0200
@@ -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"
--- libgomp/testsuite/libgomp.c++/range-for-5.C.jj	2024-09-24 14:03:26.801465614 +0200
+++ libgomp/testsuite/libgomp.c++/range-for-5.C	2024-09-24 14:02:53.986913469 +0200
@@ -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"