https://gcc.gnu.org/g:650e91566561870f3d1c8d5b92e6613296ee1a8d

commit r15-3840-g650e91566561870f3d1c8d5b92e6613296ee1a8d
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Tue Sep 24 20:19:50 2024 +0200

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

Diff:
---
 gcc/c-family/c-cppbuiltin.cc                     |   6 +-
 gcc/c-family/c-omp.cc                            |   1 +
 gcc/c-family/c-opts.cc                           |  15 ++
 gcc/c-family/c.opt                               |   4 +
 gcc/cp/call.cc                                   |   6 +
 gcc/cp/cp-tree.h                                 |   4 +-
 gcc/cp/decl.cc                                   |   5 +
 gcc/cp/parser.cc                                 |  57 +++++-
 gcc/cp/pt.cc                                     |  39 +++-
 gcc/cp/semantics.cc                              |  56 +++--
 gcc/doc/invoke.texi                              |  13 +-
 gcc/omp-general.cc                               |   2 +
 gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C          |   4 +-
 gcc/testsuite/g++.dg/cpp23/range-for1.C          | 222 ++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp23/range-for2.C          | 231 +++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp23/range-for3.C          |   7 +
 gcc/testsuite/g++.dg/cpp23/range-for4.C          |   7 +
 gcc/testsuite/g++.dg/cpp23/range-for5.C          |   8 +
 gcc/testsuite/g++.dg/cpp23/range-for6.C          |   8 +
 gcc/testsuite/g++.dg/cpp23/range-for7.C          |   6 +
 gcc/testsuite/g++.dg/cpp23/range-for8.C          |   6 +
 gcc/testsuite/g++.dg/cpp26/feat-cxx26.C          |   4 +-
 gcc/testsuite/g++.dg/warn/Wdangling-reference4.C |   4 +-
 libgomp/testsuite/libgomp.c++/range-for-1.C      | 250 +++++++++++++++++++++++
 libgomp/testsuite/libgomp.c++/range-for-2.C      |   6 +
 libgomp/testsuite/libgomp.c++/range-for-3.C      |   7 +
 libgomp/testsuite/libgomp.c++/range-for-4.C      |   7 +
 libgomp/testsuite/libgomp.c++/range-for-5.C      |   7 +
 28 files changed, 961 insertions(+), 31 deletions(-)

diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index 97c3ecb7d161..73a07055a86e 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -1034,7 +1034,11 @@ c_cpp_builtins (cpp_reader *pfile)
          cpp_define (pfile, "__cpp_fold_expressions=201603L");
          if (cxx_dialect <= cxx17)
            cpp_define (pfile, "__cpp_nontype_template_args=201411L");
-         cpp_define (pfile, "__cpp_range_based_for=201603L");
+         if (!flag_range_for_ext_temps)
+           cpp_define (pfile, "__cpp_range_based_for=201603L");
+          else
+           /* This is the C++23 or -std=c++17 -frange-for-ext-temps value.  */
+           cpp_define (pfile, "__cpp_range_based_for=202211L");
          if (cxx_dialect <= cxx17)
            cpp_define (pfile, "__cpp_constexpr=201603L");
          cpp_define (pfile, "__cpp_if_constexpr=201606L");
diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc
index b5ce1466e5d1..620a3c1353a6 100644
--- a/gcc/c-family/c-omp.cc
+++ b/gcc/c-family/c-omp.cc
@@ -1617,6 +1617,7 @@ c_find_nested_loop_xform_r (tree *tp, int *walk_subtrees, 
void *)
       *walk_subtrees = 1;
       break;
     case TRY_FINALLY_EXPR:
+    case CLEANUP_POINT_EXPR:
       *walk_subtrees = 1;
       break;
     default:
diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc
index 69dd52810205..86163ddb4edd 100644
--- a/gcc/c-family/c-opts.cc
+++ b/gcc/c-family/c-opts.cc
@@ -1160,6 +1160,21 @@ c_common_post_options (const char **pfilename)
   if (cxx_dialect >= cxx20)
     flag_concepts = 1;
 
+  /* Enable lifetime extension of range based for temporaries for C++23.
+     Diagnose -std=c++23 -fno-range-for-ext-temps.  */
+  if (cxx_dialect >= cxx23)
+    {
+      if (OPTION_SET_P (flag_range_for_ext_temps)
+         && !flag_range_for_ext_temps)
+       error ("%<-fno-range-for-ext-temps%> is incompatible with C++23");
+      flag_range_for_ext_temps = 1;
+    }
+  /* Otherwise default to enabled in GNU modes but allow user to override.  */
+  else if (cxx_dialect >= cxx11
+          && !flag_iso
+          && !OPTION_SET_P (flag_range_for_ext_temps))
+    flag_range_for_ext_temps = 1;
+
   /* -fimmediate-escalation has no effect when immediate functions are not
      supported.  */
   if (flag_immediate_escalation && cxx_dialect < cxx20)
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 98a35f043c76..b5983093e24a 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -2209,6 +2209,10 @@ fprintf-return-value
 C ObjC C++ ObjC++ LTO Optimization Var(flag_printf_return_value) Init(1)
 Treat known sprintf return values as constants.
 
+frange-for-ext-temps
+C++ ObjC++ Var(flag_range_for_ext_temps)
+Enable lifetime extension of range based for temporaries.
+
 freplace-objc-classes
 ObjC ObjC++ LTO Var(flag_replace_objc_classes)
 Used in Fix-and-Continue mode to indicate that object files may be swapped in 
at runtime.
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 70783baac242..309ab01d12d3 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -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))
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 4809576b654d..7c438eca16d6 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7474,7 +7474,8 @@ extern bool maybe_clone_body                      (tree);
 extern tree cp_convert_range_for (tree, tree, tree, cp_decomp *, bool,
                                  tree, bool);
 extern void cp_convert_omp_range_for (tree &, tree &, tree &,
-                                     tree &, tree &, tree &, tree &, tree &);
+                                     tree &, tree &, tree &, tree &, tree &,
+                                     bool);
 extern void cp_finish_omp_range_for (tree, tree);
 extern bool cp_maybe_parse_omp_decl (tree, tree);
 extern bool parsing_nsdmi (void);
@@ -7809,6 +7810,7 @@ extern tree begin_for_stmt                        (tree, 
tree);
 extern void finish_init_stmt                   (tree);
 extern void finish_for_cond            (tree, tree, bool, tree, bool);
 extern void finish_for_expr                    (tree, tree);
+extern void find_range_for_decls               (tree[3]);
 extern void finish_for_stmt                    (tree);
 extern tree begin_range_for_stmt               (tree, tree);
 extern void finish_range_for_decl              (tree, tree, tree);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 2190ede745b8..6a7ba416cf8a 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -8157,6 +8157,11 @@ initialize_local_var (tree decl, tree init, bool decomp)
             code emitted by cp_finish_decomp.  */
          if (decomp)
            current_stmt_tree ()->stmts_are_full_exprs_p = 0;
+         /* P2718R0 - avoid CLEANUP_POINT_EXPR for range-for-initializer,
+            temporaries from there should have lifetime extended.  */
+         else if (DECL_NAME (decl) == for_range__identifier
+                  && flag_range_for_ext_temps)
+           current_stmt_tree ()->stmts_are_full_exprs_p = 0;
          else
            current_stmt_tree ()->stmts_are_full_exprs_p = 1;
          finish_expr_stmt (init);
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 35c266659e46..83ae38a33ab2 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -14480,6 +14480,15 @@ cp_convert_range_for (tree statement, tree range_decl, 
tree range_expr,
        {
          range_temp = build_range_temp (range_expr);
          pushdecl (range_temp);
+         if (flag_range_for_ext_temps)
+           {
+             /* P2718R0 - put the range_temp declaration and everything
+                until end of the range for body into an extra STATEMENT_LIST
+                which will have CLEANUP_POINT_EXPR around it, so that all
+                temporaries are destroyed at the end of it.  */
+             gcc_assert (FOR_INIT_STMT (statement) == NULL_TREE);
+             FOR_INIT_STMT (statement) = push_stmt_list ();
+           }
          cp_finish_decl (range_temp, range_expr,
                          /*is_constant_init*/false, NULL_TREE,
                          LOOKUP_ONLYCONVERTING);
@@ -44629,7 +44638,8 @@ cp_parser_omp_for_loop_init (cp_parser *parser,
 void
 cp_convert_omp_range_for (tree &this_pre_body, tree &sl,
                          tree &decl, tree &orig_decl, tree &init,
-                         tree &orig_init, tree &cond, tree &incr)
+                         tree &orig_init, tree &cond, tree &incr,
+                         bool tmpl_p)
 {
   tree begin, end, range_temp_decl = NULL_TREE;
   tree iter_type, begin_expr, end_expr;
@@ -44687,11 +44697,29 @@ cp_convert_omp_range_for (tree &this_pre_body, tree 
&sl,
       else
        {
          range_temp = build_range_temp (init);
-         DECL_NAME (range_temp) = NULL_TREE;
+         tree name = DECL_NAME (range_temp);
+         /* Temporarily clear DECL_NAME of the __for_range temporary.
+            While it contains a space at the end and outside of templates
+            it works even without doing this, when cp_convert_omp_range_for
+            is called from tsubst_omp_for_iterator, all the associated loops
+            are in a single scope and so loop nests with 2 or more range
+            based for loops would error.  */
+         if (tmpl_p)
+           DECL_NAME (range_temp) = NULL_TREE;
          pushdecl (range_temp);
+         /* Restore the name back.  This is needed for cp_finish_decl
+            lifetime extension of temporaries, and can be helpful for user
+            during debugging after the DECL_NAME is changed to __for_range
+            without space at the end.  */
+         if (tmpl_p)
+           DECL_NAME (range_temp) = name;
          cp_finish_decl (range_temp, init,
                          /*is_constant_init*/false, NULL_TREE,
                          LOOKUP_ONLYCONVERTING);
+         /* And clear the name again.  pop_scope requires that the name
+            used during pushdecl didn't change.  */
+         if (tmpl_p)
+           DECL_NAME (range_temp) = NULL_TREE;
          range_temp_decl = range_temp;
          range_temp = convert_from_reference (range_temp);
        }
@@ -44791,7 +44819,7 @@ cp_convert_omp_range_for (tree &this_pre_body, tree &sl,
      the whole loop nest.  The remaining elements are decls of derived
      decomposition variables that are bound inside the loop body.  This
      structure is further mangled by finish_omp_for into the form required
-     for the OMP_FOR_ORIG_DECLS field of the OMP_FOR tree node.  */\
+     for the OMP_FOR_ORIG_DECLS field of the OMP_FOR tree node.  */
   unsigned decomp_cnt = decomp ? decomp->count : 0;
   tree v = make_tree_vec (decomp_cnt + 3);
   TREE_VEC_ELT (v, 0) = range_temp_decl;
@@ -45360,7 +45388,7 @@ cp_parser_omp_loop_nest (cp_parser *parser, bool *if_p)
 
          cp_convert_omp_range_for (this_pre_body, sl, decl,
                                    orig_decl, init, orig_init,
-                                   cond, incr);
+                                   cond, incr, false);
 
          if (omp_for_parse_state->ordered_cl)
            error_at (OMP_CLAUSE_LOCATION (omp_for_parse_state->ordered_cl),
@@ -45623,11 +45651,30 @@ cp_parser_omp_loop_nest (cp_parser *parser, bool 
*if_p)
 
   /* Pop and remember the init block.  */
   if (sl)
-    add_stmt (pop_stmt_list (sl));
+    {
+      sl = pop_stmt_list (sl);
+      /* P2718R0 - Add CLEANUP_POINT_EXPR so that temporaries in
+        for-range-initializer whose lifetime is extended are destructed
+        here.  */
+      if (flag_range_for_ext_temps
+         && is_range_for
+         && !processing_template_decl)
+       sl = maybe_cleanup_point_expr_void (sl);
+      add_stmt (sl);
+    }
+  tree range_for_decl[3] = { NULL_TREE, NULL_TREE, NULL_TREE };
+  if (is_range_for && !processing_template_decl)
+    find_range_for_decls (range_for_decl);
   finish_compound_stmt (init_scope);
   init_block = pop_stmt_list (init_block);
   omp_for_parse_state->init_blockv[depth] = init_block;
 
+  if (is_range_for && !processing_template_decl)
+    for (int i = 0; i < 3; i++)
+      if (range_for_decl[i])
+       DECL_NAME (range_for_decl[i])
+         = cp_global_trees[CPTI_FOR_RANGE_IDENTIFIER + i];
+
   /* Return the init placeholder rather than the remembered init block.
      Again, this is just a unique cookie that will be used to reassemble
      code pieces when the entire omp for statement has been parsed.  */
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index e826206be164..a3a697dd1263 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -18127,7 +18127,7 @@ tsubst_omp_for_iterator (tree t, int i, tree declv, 
tree &orig_declv,
       tree orig_decl = NULL_TREE;
       tree init_sl = NULL_TREE;
       cp_convert_omp_range_for (this_pre_body, init_sl, decl, orig_decl, init,
-                               orig_init, cond, incr);
+                               orig_init, cond, incr, true);
       if (orig_decl)
        {
          if (orig_declv == NULL_TREE)
@@ -19156,6 +19156,18 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
        RECUR (OMP_FOR_PRE_BODY (t));
        pre_body = pop_stmt_list (pre_body);
 
+       tree sl = NULL_TREE;
+       if (flag_range_for_ext_temps
+           && OMP_FOR_INIT (t) != NULL_TREE
+           && !processing_template_decl)
+         for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (t)); i++)
+           if (TREE_VEC_ELT (OMP_FOR_INIT (t), i)
+               && TREE_VEC_ELT (OMP_FOR_COND (t), i) == global_namespace)
+             {
+               sl = push_stmt_list ();
+               break;
+             }
+
        if (OMP_FOR_INIT (t) != NULL_TREE)
          for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (t)); i++)
            {
@@ -19211,9 +19223,34 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
            add_stmt (t);
          }
 
+       if (sl)
+         {
+           /* P2718R0 - Add CLEANUP_POINT_EXPR so that temporaries in
+              for-range-initializer whose lifetime is extended are destructed
+              here.  */
+           sl = pop_stmt_list (sl);
+           sl = maybe_cleanup_point_expr_void (sl);
+           add_stmt (sl);
+         }
+
        add_stmt (finish_omp_for_block (finish_omp_structured_block (stmt),
                                        t));
        pop_omp_privatization_clauses (r);
+
+       if (any_range_for && !processing_template_decl && t)
+         for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (t)); i++)
+           if (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (t), i)
+               && TREE_CODE (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (t),
+                                           i)) == TREE_LIST)
+             {
+               tree v = TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (t), i);
+               if (TREE_CHAIN (v) == NULL_TREE
+                   || TREE_CODE (TREE_CHAIN (v)) != TREE_VEC)
+                 continue;
+               v = TREE_VEC_ELT (TREE_CHAIN (v), 0);
+               gcc_assert (VAR_P (v) && DECL_NAME (v) == NULL_TREE);
+               DECL_NAME (v) = for_range_identifier;
+             }
       }
       break;
 
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 8219d6410b8e..a061704e1a27 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -1637,6 +1637,33 @@ finish_for_expr (tree expr, tree for_stmt)
   FOR_EXPR (for_stmt) = expr;
 }
 
+/* During parsing of the body, range for uses "__for_{range,begin,end} "
+   decl names to make those unaccessible by code in the body.
+   Find those decls and store into RANGE_FOR_DECL array, so that they
+   can be changed later to ones with underscore instead of space, so that
+   it can be inspected in the debugger.  */
+
+void
+find_range_for_decls (tree range_for_decl[3])
+{
+  gcc_assert (CPTI_FOR_BEGIN__IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 1
+             && CPTI_FOR_END__IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 2
+             && CPTI_FOR_RANGE_IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 3
+             && CPTI_FOR_BEGIN_IDENTIFIER == CPTI_FOR_BEGIN__IDENTIFIER + 3
+             && CPTI_FOR_END_IDENTIFIER == CPTI_FOR_END__IDENTIFIER + 3);
+  for (int i = 0; i < 3; i++)
+    {
+      tree id = cp_global_trees[CPTI_FOR_RANGE__IDENTIFIER + i];
+      if (IDENTIFIER_BINDING (id)
+         && IDENTIFIER_BINDING (id)->scope == current_binding_level)
+       {
+         range_for_decl[i] = IDENTIFIER_BINDING (id)->value;
+         gcc_assert (VAR_P (range_for_decl[i])
+                     && DECL_ARTIFICIAL (range_for_decl[i]));
+       }
+    }
+}
+
 /* Finish the body of a for-statement, which may be given by
    FOR_STMT.  The increment-EXPR for the loop must be
    provided.
@@ -1670,21 +1697,20 @@ finish_for_stmt (tree for_stmt)
      Change it to ones with underscore instead of space, so that it can
      be inspected in the debugger.  */
   tree range_for_decl[3] = { NULL_TREE, NULL_TREE, NULL_TREE };
-  gcc_assert (CPTI_FOR_BEGIN__IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 1
-             && CPTI_FOR_END__IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 2
-             && CPTI_FOR_RANGE_IDENTIFIER == CPTI_FOR_RANGE__IDENTIFIER + 3
-             && CPTI_FOR_BEGIN_IDENTIFIER == CPTI_FOR_BEGIN__IDENTIFIER + 3
-             && CPTI_FOR_END_IDENTIFIER == CPTI_FOR_END__IDENTIFIER + 3);
-  for (int i = 0; i < 3; i++)
-    {
-      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]));
-       }
+  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 stmt = pop_stmt_list (FOR_INIT_STMT (for_stmt));
+      FOR_INIT_STMT (for_stmt) = NULL_TREE;
+      stmt = build_stmt (EXPR_LOCATION (for_stmt), EXPR_STMT, stmt);
+      stmt = maybe_cleanup_point_expr_void (stmt);
+      add_stmt (stmt);
     }
 
   add_stmt (do_poplevel (scope));
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 1f9f3386bf97..bdbbea53666e 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -214,7 +214,7 @@ in the following sections.
 @xref{C++ Dialect Options,,Options Controlling C++ Dialect}.
 @gccoptlist{-fabi-version=@var{n}  -fno-access-control
 -faligned-new=@var{n}  -fargs-in-order=@var{n}  -fchar8_t  -fcheck-new
--fconstexpr-depth=@var{n}  -fconstexpr-cache-depth=@var{n}
+-fconcepts  -fconstexpr-depth=@var{n}  -fconstexpr-cache-depth=@var{n}
 -fconstexpr-loop-limit=@var{n}  -fconstexpr-ops-limit=@var{n}
 -fno-elide-constructors
 -fno-enforce-eh-specs
@@ -233,7 +233,7 @@ in the following sections.
 -fnew-ttp-matching
 -fno-nonansi-builtins  -fnothrow-opt  -fno-operator-names
 -fno-optional-diags
--fno-pretty-templates
+-fno-pretty-templates  -frange-for-ext-temps
 -fno-rtti  -fsized-deallocation
 -ftemplate-backtrace-limit=@var{n}
 -ftemplate-depth=@var{n}
@@ -3614,6 +3614,15 @@ the default template arguments for that template.  If 
either of these
 behaviors make it harder to understand the error message rather than
 easier, you can use @option{-fno-pretty-templates} to disable them.
 
+@opindex frange-for-ext-temps
+@item -frange-for-ext-temps
+Enable lifetime extension of C++ range based for temporaries.
+With @option{-std=c++23} and above this is part of the language standard,
+so lifetime of the temporaries is extended until the end of the loop
+regardless of this option.  This option allows enabling that behavior also
+in earlier versions of the standard and is enabled by default in the
+GNU dialects, from @option{-std=gnu++11} until @option{-std=gnu++20}.
+
 @opindex fno-rtti
 @opindex frtti
 @item -fno-rtti
diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc
index 9713e684e830..f4c5f5770474 100644
--- a/gcc/omp-general.cc
+++ b/gcc/omp-general.cc
@@ -972,6 +972,7 @@ find_combined_omp_for (tree *tp, int *walk_subtrees, void 
*data)
       *walk_subtrees = 1;
       break;
     case TRY_FINALLY_EXPR:
+    case CLEANUP_POINT_EXPR:
       pdata[0] = tp;
       *walk_subtrees = 1;
       break;
@@ -4226,6 +4227,7 @@ find_nested_loop_xform (tree *tp, int *walk_subtrees, 
void *data)
       *walk_subtrees = 1;
       break;
     case TRY_FINALLY_EXPR:
+    case CLEANUP_POINT_EXPR:
       pdata[0] = tp;
       *walk_subtrees = 1;
       break;
diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C 
b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
index a1ddc81cefde..4033552b2ebc 100644
--- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
+++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
@@ -43,8 +43,8 @@
 
 #ifndef __cpp_range_based_for
 #  error "__cpp_range_based_for"
-#elif __cpp_range_based_for != 201603
-#  error "__cpp_range_based_for != 201603"
+#elif __cpp_range_based_for != 202211
+#  error "__cpp_range_based_for != 202211"
 #endif
 
 #ifndef __cpp_decltype
diff --git a/gcc/testsuite/g++.dg/cpp23/range-for1.C 
b/gcc/testsuite/g++.dg/cpp23/range-for1.C
new file mode 100644
index 000000000000..2aa8a5e93328
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/range-for1.C
@@ -0,0 +1,222 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run { target c++11 } }
+
+#ifndef RANGE_FOR_EXT_TEMPS
+#define RANGE_FOR_EXT_TEMPS (__cpp_range_based_for >= 202211L)
+#else
+#if __cplusplus >= 201703L
+#if RANGE_FOR_EXT_TEMPS
+static_assert (__cpp_range_based_for >= 202211L, "");
+#else
+static_assert (__cpp_range_based_for >= 201603L
+              && __cpp_range_based_for < 202211L, "");
+#endif
+#else
+static_assert (__cpp_range_based_for >= 200907L
+              && __cpp_range_based_for < 201603L, "");
+#endif
+#endif
+
+extern "C" void abort ();
+void check (bool);
+
+struct S
+{
+  S () { ++s; }
+  S (const S &) { ++s; }
+  ~S () { check (true); --s; }
+  static int s;
+};
+
+int S::s = -1;
+S sv;
+
+struct T
+{
+  T (const S &, const S &) { ++t; }
+  T (const T &) { ++t; }
+  ~T () { check (false); --t; }
+  static int t;
+};
+
+int T::t = -1;
+T tv (sv, sv);
+int a[4];
+int c;
+
+void
+check (bool is_s)
+{
+  if (c)
+    {
+      if (is_s)
+       {
+         if (T::t != (c == 1))
+           abort ();
+       }
+      else
+       {
+         if (S::s != (c == 1 ? 0 : 2))
+           abort ();
+       }
+    }
+}
+
+template <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> ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/range-for2.C 
b/gcc/testsuite/g++.dg/cpp23/range-for2.C
new file mode 100644
index 000000000000..e40e6d32a27b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/range-for2.C
@@ -0,0 +1,231 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run { target c++11 } }
+
+#ifndef RANGE_FOR_EXT_TEMPS
+#define RANGE_FOR_EXT_TEMPS (__cpp_range_based_for >= 202211L)
+#endif
+
+extern "C" void abort ();
+
+int a[4];
+
+template <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> ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/range-for3.C 
b/gcc/testsuite/g++.dg/cpp23/range-for3.C
new file mode 100644
index 000000000000..301e25886ec6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/range-for3.C
@@ -0,0 +1,7 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run { target c++11 } }
+// Verify -frange-for-ext-temps is set by default in -std=gnu++* modes.
+// { dg-options "" }
+
+#define RANGE_FOR_EXT_TEMPS 1
+#include "range-for1.C"
diff --git a/gcc/testsuite/g++.dg/cpp23/range-for4.C 
b/gcc/testsuite/g++.dg/cpp23/range-for4.C
new file mode 100644
index 000000000000..f8c380d32c72
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/range-for4.C
@@ -0,0 +1,7 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run { target c++11 } }
+// Verify -frange-for-ext-temps is set by default in -std=gnu++* modes.
+// { dg-options "" }
+
+#define RANGE_FOR_EXT_TEMPS 1
+#include "range-for2.C"
diff --git a/gcc/testsuite/g++.dg/cpp23/range-for5.C 
b/gcc/testsuite/g++.dg/cpp23/range-for5.C
new file mode 100644
index 000000000000..c2cd1248bf68
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/range-for5.C
@@ -0,0 +1,8 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run { target { c++11 && c++20_down } } }
+// { dg-options "-fno-range-for-ext-temps" }
+
+#if __cplusplus <= 202002L
+#define RANGE_FOR_EXT_TEMPS 0
+#endif
+#include "range-for1.C"
diff --git a/gcc/testsuite/g++.dg/cpp23/range-for6.C 
b/gcc/testsuite/g++.dg/cpp23/range-for6.C
new file mode 100644
index 000000000000..8bf8656e5ef7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/range-for6.C
@@ -0,0 +1,8 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run { target { c++11 && c++20_down } } }
+// { dg-options "-fno-range-for-ext-temps" }
+
+#if __cplusplus <= 202002L
+#define RANGE_FOR_EXT_TEMPS 0
+#endif
+#include "range-for2.C"
diff --git a/gcc/testsuite/g++.dg/cpp23/range-for7.C 
b/gcc/testsuite/g++.dg/cpp23/range-for7.C
new file mode 100644
index 000000000000..99d9bd85bc36
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/range-for7.C
@@ -0,0 +1,6 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run { target c++17_only } }
+// { dg-options "-std=c++17 -frange-for-ext-temps" }
+
+#define RANGE_FOR_EXT_TEMPS 1
+#include "range-for1.C"
diff --git a/gcc/testsuite/g++.dg/cpp23/range-for8.C 
b/gcc/testsuite/g++.dg/cpp23/range-for8.C
new file mode 100644
index 000000000000..3b2efbc7e41a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/range-for8.C
@@ -0,0 +1,6 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run { target c++11_only } }
+// { dg-options "-std=c++11 -frange-for-ext-temps" }
+
+#define RANGE_FOR_EXT_TEMPS 1
+#include "range-for2.C"
diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C 
b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
index 52c89e498fb0..c387a7dfe609 100644
--- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
+++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
@@ -43,8 +43,8 @@
 
 #ifndef __cpp_range_based_for
 #  error "__cpp_range_based_for"
-#elif __cpp_range_based_for != 201603
-#  error "__cpp_range_based_for != 201603"
+#elif __cpp_range_based_for != 202211
+#  error "__cpp_range_based_for != 202211"
 #endif
 
 #ifndef __cpp_decltype
diff --git a/gcc/testsuite/g++.dg/warn/Wdangling-reference4.C 
b/gcc/testsuite/g++.dg/warn/Wdangling-reference4.C
index 0343bcf226b6..ada70dd683a3 100644
--- a/gcc/testsuite/g++.dg/warn/Wdangling-reference4.C
+++ b/gcc/testsuite/g++.dg/warn/Wdangling-reference4.C
@@ -1,5 +1,5 @@
 // { dg-do compile { target c++17 } }
-// { dg-options "-Wdangling-reference" }
+// { dg-additional-options "-Wdangling-reference" }
 // { dg-skip-if "requires hosted libstdc++ for string" { ! hostedlib } }
 // Check that we warn here even without -Wsystem-headers.
 
@@ -11,5 +11,5 @@ auto f() -> std::optional<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 } }
 }
diff --git a/libgomp/testsuite/libgomp.c++/range-for-1.C 
b/libgomp/testsuite/libgomp.c++/range-for-1.C
new file mode 100644
index 000000000000..c92f9c4e1453
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/range-for-1.C
@@ -0,0 +1,250 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run }
+// { dg-additional-options "-std=c++17" }
+// { dg-require-effective-target tls_runtime }
+
+#ifndef RANGE_FOR_EXT_TEMPS
+#define RANGE_FOR_EXT_TEMPS (__cpp_range_based_for >= 202211L)
+#endif
+
+extern "C" void abort ();
+void check (bool);
+
+struct S
+{
+  S () { ++s; }
+  S (const S &) { ++s; }
+  ~S () { check (true); --s; }
+  [[omp::decl (threadprivate)]] static int s;
+};
+int S::s;
+S sv;
+struct T
+{
+  T (const S &, const S &) { ++t; }
+  T (const T &) { ++t; }
+  ~T () { check (false); --t; }
+  [[omp::decl (threadprivate)]] static int t;
+};
+int T::t;
+T tv (sv, sv);
+int a[4];
+int c;
+
+void
+check (bool is_s)
+{
+  if (c)
+    {
+      if (is_s)
+       {
+         if (T::t != (c == 1))
+           abort ();
+       }
+      else
+       {
+         if (S::s != (c == 1 ? 0 : 2))
+           abort ();
+       }
+    }
+}
+
+template <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> ();
+}
diff --git a/libgomp/testsuite/libgomp.c++/range-for-2.C 
b/libgomp/testsuite/libgomp.c++/range-for-2.C
new file mode 100644
index 000000000000..8c25140cdd5f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/range-for-2.C
@@ -0,0 +1,6 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run }
+// { dg-additional-options "-std=c++23" }
+// { dg-require-effective-target tls_runtime }
+
+#include "range-for-1.C"
diff --git a/libgomp/testsuite/libgomp.c++/range-for-3.C 
b/libgomp/testsuite/libgomp.c++/range-for-3.C
new file mode 100644
index 000000000000..919fe85b374d
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/range-for-3.C
@@ -0,0 +1,7 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run }
+// { dg-additional-options "-std=c++17 -frange-for-ext-temps" }
+// { dg-require-effective-target tls_runtime }
+
+#define RANGE_FOR_EXT_TEMPS 1
+#include "range-for-1.C"
diff --git a/libgomp/testsuite/libgomp.c++/range-for-4.C 
b/libgomp/testsuite/libgomp.c++/range-for-4.C
new file mode 100644
index 000000000000..3c10e7349af7
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/range-for-4.C
@@ -0,0 +1,7 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run }
+// { dg-additional-options "-std=gnu++17" }
+// { dg-require-effective-target tls_runtime }
+
+#define RANGE_FOR_EXT_TEMPS 1
+#include "range-for-1.C"
diff --git a/libgomp/testsuite/libgomp.c++/range-for-5.C 
b/libgomp/testsuite/libgomp.c++/range-for-5.C
new file mode 100644
index 000000000000..d8c84c326695
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/range-for-5.C
@@ -0,0 +1,7 @@
+// P2718R0 - Wording for P2644R1 Fix for Range-based for Loop
+// { dg-do run }
+// { dg-additional-options "-std=gnu++17 -fno-range-for-ext-temps" }
+// { dg-require-effective-target tls_runtime }
+
+#define RANGE_FOR_EXT_TEMPS 0
+#include "range-for-1.C"

Reply via email to