diff mbox series

C++ PATCH for c++/87152, range-based for loops with initializer broken in templates

Message ID 20180907032944.GR12638@redhat.com
State New
Headers show
Series C++ PATCH for c++/87152, range-based for loops with initializer broken in templates | expand

Commit Message

Marek Polacek Sept. 7, 2018, 3:29 a.m. UTC
Turned out our range-based for loops with initializer are completely broken
in templates, because I neglected to add the init-statement to RANGE_FOR_STMT.
range-for18.C should verify we put it into the right scope.

tsubst_expr then needs to recurse on it to avoid the ICE.

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

2018-09-06  Marek Polacek  <polacek@redhat.com>

	PR c++/87152 - range-based for loops with initializer broken in templates.
	* constexpr.c (potential_constant_expression_1) <case RANGE_FOR_STMT>:
	Recur into RANGE_FOR_INIT_STMT.
	* cp-tree.def: Add RANGE_FOR_INIT_STMT to RANGE_FOR_STMT.
	* cp-tree.h (RANGE_FOR_INIT_STMT): Define.
	* dump.c (cp_dump_tree) <case RANGE_FOR_STMT>: Also dump
	RANGE_FOR_INIT_STMT.
	* pt.c (tsubst_expr) <case RANGE_FOR_STMT>: Recur into
	RANGE_FOR_INIT_STMT.
	* semantics.c (begin_range_for_stmt): Adjust call to build_stmt.
	Do put the init statement in RANGE_FOR_INIT_STMT.
	(finish_range_for_decl): Pop it for templates.

	* g++.dg/cpp2a/range-for11.C: New test.
	* g++.dg/cpp2a/range-for12.C: New test.
	* g++.dg/cpp2a/range-for13.C: New test.
	* g++.dg/cpp2a/range-for14.C: New test.
	* g++.dg/cpp2a/range-for15.C: New test.
	* g++.dg/cpp2a/range-for16.C: New test.
	* g++.dg/cpp2a/range-for17.C: New test.
	* g++.dg/cpp2a/range-for18.C: New test.
	* g++.dg/parse/error61.C (foo): Adjust dg-error.

Comments

Jason Merrill Sept. 7, 2018, 12:32 p.m. UTC | #1
OK

On Fri, Sep 7, 2018 at 4:29 AM, Marek Polacek <polacek@redhat.com> wrote:
> Turned out our range-based for loops with initializer are completely broken
> in templates, because I neglected to add the init-statement to RANGE_FOR_STMT.
> range-for18.C should verify we put it into the right scope.
>
> tsubst_expr then needs to recurse on it to avoid the ICE.
>
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
>
> 2018-09-06  Marek Polacek  <polacek@redhat.com>
>
>         PR c++/87152 - range-based for loops with initializer broken in templates.
>         * constexpr.c (potential_constant_expression_1) <case RANGE_FOR_STMT>:
>         Recur into RANGE_FOR_INIT_STMT.
>         * cp-tree.def: Add RANGE_FOR_INIT_STMT to RANGE_FOR_STMT.
>         * cp-tree.h (RANGE_FOR_INIT_STMT): Define.
>         * dump.c (cp_dump_tree) <case RANGE_FOR_STMT>: Also dump
>         RANGE_FOR_INIT_STMT.
>         * pt.c (tsubst_expr) <case RANGE_FOR_STMT>: Recur into
>         RANGE_FOR_INIT_STMT.
>         * semantics.c (begin_range_for_stmt): Adjust call to build_stmt.
>         Do put the init statement in RANGE_FOR_INIT_STMT.
>         (finish_range_for_decl): Pop it for templates.
>
>         * g++.dg/cpp2a/range-for11.C: New test.
>         * g++.dg/cpp2a/range-for12.C: New test.
>         * g++.dg/cpp2a/range-for13.C: New test.
>         * g++.dg/cpp2a/range-for14.C: New test.
>         * g++.dg/cpp2a/range-for15.C: New test.
>         * g++.dg/cpp2a/range-for16.C: New test.
>         * g++.dg/cpp2a/range-for17.C: New test.
>         * g++.dg/cpp2a/range-for18.C: New test.
>         * g++.dg/parse/error61.C (foo): Adjust dg-error.
>
> diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
> index f646519135f..6c2689064f3 100644
> --- gcc/cp/constexpr.c
> +++ gcc/cp/constexpr.c
> @@ -5767,6 +5767,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
>        return true;
>
>      case RANGE_FOR_STMT:
> +      if (!RECUR (RANGE_FOR_INIT_STMT (t), any))
> +       return false;
>        if (!RECUR (RANGE_FOR_EXPR (t), any))
>         return false;
>        if (!RECUR (RANGE_FOR_BODY (t), any))
> diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
> index 1b0326f4e81..c64225ded6f 100644
> --- gcc/cp/cp-tree.def
> +++ gcc/cp/cp-tree.def
> @@ -301,9 +301,10 @@ DEFTREECODE (IF_STMT, "if_stmt", tcc_statement, 4)
>  DEFTREECODE (FOR_STMT, "for_stmt", tcc_statement, 5)
>
>  /* Used to represent a range-based `for' statement. The operands are
> -   RANGE_FOR_DECL, RANGE_FOR_EXPR, RANGE_FOR_BODY, and RANGE_FOR_SCOPE,
> -   RANGE_FOR_UNROLL respectively.  Only used in templates.  */
> -DEFTREECODE (RANGE_FOR_STMT, "range_for_stmt", tcc_statement, 5)
> +   RANGE_FOR_DECL, RANGE_FOR_EXPR, RANGE_FOR_BODY, RANGE_FOR_SCOPE,
> +   RANGE_FOR_UNROLL, and RANGE_FOR_INIT_STMT, respectively.  Only used in
> +   templates.  */
> +DEFTREECODE (RANGE_FOR_STMT, "range_for_stmt", tcc_statement, 6)
>
>  /* Used to represent a 'while' statement. The operands are WHILE_COND
>     and WHILE_BODY, respectively.  */
> diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
> index df441fca304..b78e9eb252b 100644
> --- gcc/cp/cp-tree.h
> +++ gcc/cp/cp-tree.h
> @@ -4923,6 +4923,7 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
>  #define RANGE_FOR_BODY(NODE)   TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 2)
>  #define RANGE_FOR_SCOPE(NODE)  TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 3)
>  #define RANGE_FOR_UNROLL(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 4)
> +#define RANGE_FOR_INIT_STMT(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 5)
>  #define RANGE_FOR_IVDEP(NODE)  TREE_LANG_FLAG_6 (RANGE_FOR_STMT_CHECK (NODE))
>
>  #define SWITCH_STMT_COND(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 0)
> diff --git gcc/cp/dump.c gcc/cp/dump.c
> index 9c1e5fc781a..d9b868bfaef 100644
> --- gcc/cp/dump.c
> +++ gcc/cp/dump.c
> @@ -301,6 +301,7 @@ cp_dump_tree (void* dump_info, tree t)
>
>      case RANGE_FOR_STMT:
>        dump_stmt (di, t);
> +      dump_child ("init", RANGE_FOR_INIT_STMT (t));
>        dump_child ("decl", RANGE_FOR_DECL (t));
>        dump_child ("expr", RANGE_FOR_EXPR (t));
>        dump_child ("body", RANGE_FOR_BODY (t));
> diff --git gcc/cp/pt.c gcc/cp/pt.c
> index 0a618a5447d..892a387cbc5 100644
> --- gcc/cp/pt.c
> +++ gcc/cp/pt.c
> @@ -16815,6 +16815,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
>          stmt = (processing_template_decl
>                 ? begin_range_for_stmt (NULL_TREE, NULL_TREE)
>                 : begin_for_stmt (NULL_TREE, NULL_TREE));
> +       RECUR (RANGE_FOR_INIT_STMT (t));
>          decl = RANGE_FOR_DECL (t);
>          decl = tsubst (decl, args, complain, in_decl);
>          maybe_push_decl (decl);
> diff --git gcc/cp/semantics.c gcc/cp/semantics.c
> index 676de011868..f3e5d83b1ef 100644
> --- gcc/cp/semantics.c
> +++ gcc/cp/semantics.c
> @@ -1101,8 +1101,8 @@ begin_range_for_stmt (tree scope, tree init)
>  {
>    begin_maybe_infinite_loop (boolean_false_node);
>
> -  tree r = build_stmt (input_location, RANGE_FOR_STMT,
> -                      NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
> +  tree r = build_stmt (input_location, RANGE_FOR_STMT, NULL_TREE, NULL_TREE,
> +                      NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
>
>    if (scope == NULL_TREE)
>      {
> @@ -1110,22 +1110,23 @@ begin_range_for_stmt (tree scope, tree init)
>        scope = begin_for_scope (&init);
>      }
>
> -  /* RANGE_FOR_STMTs do not use nor save the init tree, so we
> -     pop it now.  */
> -  if (init)
> -    pop_stmt_list (init);
> +  /* Since C++20, RANGE_FOR_STMTs can use the init tree, so save it.  */
> +  RANGE_FOR_INIT_STMT (r) = init;
>    RANGE_FOR_SCOPE (r) = scope;
>
>    return r;
>  }
>
>  /* Finish the head of a range-based for statement, which may
> -   be given by RANGE_FOR_STMT. DECL must be the declaration
> +   be given by RANGE_FOR_STMT.  DECL must be the declaration
>     and EXPR must be the loop expression. */
>
>  void
>  finish_range_for_decl (tree range_for_stmt, tree decl, tree expr)
>  {
> +  if (processing_template_decl)
> +    RANGE_FOR_INIT_STMT (range_for_stmt)
> +      = pop_stmt_list (RANGE_FOR_INIT_STMT (range_for_stmt));
>    RANGE_FOR_DECL (range_for_stmt) = decl;
>    RANGE_FOR_EXPR (range_for_stmt) = expr;
>    add_stmt (range_for_stmt);
> diff --git gcc/testsuite/g++.dg/cpp2a/range-for11.C gcc/testsuite/g++.dg/cpp2a/range-for11.C
> index e69de29bb2d..5f0a80318ab 100644
> --- gcc/testsuite/g++.dg/cpp2a/range-for11.C
> +++ gcc/testsuite/g++.dg/cpp2a/range-for11.C
> @@ -0,0 +1,22 @@
> +// PR c++/87152
> +// { dg-do run }
> +// { dg-options "-std=c++2a" }
> +
> +template<typename>
> +int foo ()
> +{
> +  int a[] = { 1, 2, 3, 4, 5 };
> +  int j = 0;
> +  for (int i = 0; auto x : a)
> +    j += i++;
> +
> +  return j;
> +}
> +
> +int
> +main ()
> +{
> +  int j = foo<int>();
> +  if (j != 10)
> +    __builtin_abort ();
> +}
> diff --git gcc/testsuite/g++.dg/cpp2a/range-for12.C gcc/testsuite/g++.dg/cpp2a/range-for12.C
> index e69de29bb2d..09558341bb9 100644
> --- gcc/testsuite/g++.dg/cpp2a/range-for12.C
> +++ gcc/testsuite/g++.dg/cpp2a/range-for12.C
> @@ -0,0 +1,33 @@
> +// PR c++/87152
> +// { dg-do compile }
> +// { dg-options "-std=c++2a" }
> +
> +static const int a[] = { 1, 2, 3, 4, 5 };
> +extern void foo (int);
> +extern void bar (int, int);
> +
> +constexpr int
> +baz ()
> +{
> +  return 6;
> +}
> +
> +template<typename T>
> +void
> +fn1 (T i)
> +{
> +  for ((i += 2); auto x : a)
> +    foo (i);
> +
> +  for (auto j = 0, k = 0; auto x : a)
> +    bar (j + k, x);
> +
> +  for (constexpr int j = baz (); auto x : a)
> +    bar (x, j);
> +}
> +
> +void
> +do_fn1 ()
> +{
> +  fn1<int>(10);
> +}
> diff --git gcc/testsuite/g++.dg/cpp2a/range-for13.C gcc/testsuite/g++.dg/cpp2a/range-for13.C
> index e69de29bb2d..fb1ff285529 100644
> --- gcc/testsuite/g++.dg/cpp2a/range-for13.C
> +++ gcc/testsuite/g++.dg/cpp2a/range-for13.C
> @@ -0,0 +1,33 @@
> +// PR c++/87152
> +// { dg-do run }
> +// { dg-options "-std=c++2a" }
> +
> +template<typename T>
> +void foo ()
> +{
> +  int a[] = { 1, 2, 3, 4, 5 };
> +
> +  for (T i = 1; auto x : a)
> +    if (i++ != x)
> +      __builtin_abort ();
> +
> +  T i;
> +  for (i = 1; auto x : a)
> +    if (i++ != x)
> +      __builtin_abort ();
> +
> +  i = 0;
> +  for (i++; auto x : a)
> +    if (i != 1)
> +      __builtin_abort ();
> +
> +  for (T s[] = { 1, 1, 1 }; auto x : s)
> +    if (x != 1)
> +      __builtin_abort ();
> +}
> +
> +int
> +main ()
> +{
> +  foo<int>();
> +}
> diff --git gcc/testsuite/g++.dg/cpp2a/range-for14.C gcc/testsuite/g++.dg/cpp2a/range-for14.C
> index e69de29bb2d..94ff3c19153 100644
> --- gcc/testsuite/g++.dg/cpp2a/range-for14.C
> +++ gcc/testsuite/g++.dg/cpp2a/range-for14.C
> @@ -0,0 +1,24 @@
> +// PR c++/87152
> +// { dg-do run }
> +// { dg-options "-std=c++2a" }
> +
> +template<typename T>
> +void
> +fn ()
> +{
> +  T a[] = { 1, 2, 3, 4, 5 };
> +
> +  for (T i = []{ return 3; }(); auto x : a)
> +    if (i != 3)
> +      __builtin_abort ();
> +
> +  for (T i = ({ 3; }); auto x : a)
> +    if (i != 3)
> +      __builtin_abort ();
> +}
> +
> +int
> +main ()
> +{
> +  fn<int>();
> +}
> diff --git gcc/testsuite/g++.dg/cpp2a/range-for15.C gcc/testsuite/g++.dg/cpp2a/range-for15.C
> index e69de29bb2d..532b7689459 100644
> --- gcc/testsuite/g++.dg/cpp2a/range-for15.C
> +++ gcc/testsuite/g++.dg/cpp2a/range-for15.C
> @@ -0,0 +1,43 @@
> +// PR c++/87152
> +// { dg-do run }
> +// { dg-options "-std=c++2a" }
> +
> +struct A { int i; long long j; } a[64];
> +
> +template<typename T>
> +void foo ()
> +{
> +  for (T i = 0; auto &x : a)
> +    {
> +      x.i = i;
> +      x.j = 2 * i++;
> +    }
> +  for (auto & [ x, y ] : a)
> +    {
> +      x += 2;
> +      y += 3;
> +    }
> +  for (T i = 0; const auto [ u, v ] : a)
> +    {
> +      if (u != i + 2 || v != 2 * i++ + 3)
> +        __builtin_abort ();
> +    }
> +  for (T i = 0; auto [ x, y ] : a)
> +    {
> +      x += 4;
> +      y += 5;
> +      if (x != i + 6 || y != 2 * i++ + 8)
> +        __builtin_abort ();
> +    }
> +  for (T i = 0; const auto x : a)
> +    {
> +      if (x.i != i + 2 || x.j != 2 * i++ + 3)
> +        __builtin_abort ();
> +    }
> +}
> +
> +int
> +main ()
> +{
> +  foo<int>();
> +}
> diff --git gcc/testsuite/g++.dg/cpp2a/range-for16.C gcc/testsuite/g++.dg/cpp2a/range-for16.C
> index e69de29bb2d..cbfd9873387 100644
> --- gcc/testsuite/g++.dg/cpp2a/range-for16.C
> +++ gcc/testsuite/g++.dg/cpp2a/range-for16.C
> @@ -0,0 +1,36 @@
> +// PR c++/87152
> +// { dg-do run }
> +// { dg-options "-std=c++2a" }
> +
> +struct A { int i, j; };
> +
> +template<typename T>
> +void foo ()
> +{
> +  A a = { .i = 2, .j = 3 };
> +  T arr[] = { 1, 1, 1 };
> +
> +  for (auto & [ x, y ] = a; auto z : arr)
> +    if (x + z != 3 || y + z != 4)
> +      __builtin_abort ();
> +
> +  for (T d = 1; auto &z : arr)
> +    z += d;
> +
> +  for (const auto [ x, y ] = a; auto z : arr)
> +    if (x + z != 4 || y + z != 5)
> +      __builtin_abort ();
> +
> +  for (T d = 1; auto &z : arr)
> +    z += d;
> +
> +  for (auto [ x, y ] = a; auto z : arr)
> +    if (x + z != 5 || y + z != 6)
> +      __builtin_abort ();
> +}
> +
> +int
> +main ()
> +{
> +  foo<int>();
> +}
> diff --git gcc/testsuite/g++.dg/cpp2a/range-for17.C gcc/testsuite/g++.dg/cpp2a/range-for17.C
> index e69de29bb2d..2e8734b795d 100644
> --- gcc/testsuite/g++.dg/cpp2a/range-for17.C
> +++ gcc/testsuite/g++.dg/cpp2a/range-for17.C
> @@ -0,0 +1,30 @@
> +// PR c++/87152
> +// { dg-do run }
> +// { dg-options "-std=c++2a" }
> +
> +struct A { int i; long long j; } a[64];
> +
> +template<typename>
> +void foo ()
> +{
> +  A b = { 1, 2 };
> +  for (auto & [ u, v ] : a)
> +    {
> +      u = 2;
> +      v = 3;
> +    }
> +
> +  for (auto [x, y] = b; auto [ u, v ] : a)
> +    if (y + u != x + v)
> +      __builtin_abort ();
> +
> +  for (auto [x, y] = b; auto & [ u, v ] : a)
> +    if (y + u != x + v)
> +      __builtin_abort ();
> +}
> +
> +int
> +main ()
> +{
> +  foo<int>();
> +}
> diff --git gcc/testsuite/g++.dg/cpp2a/range-for18.C gcc/testsuite/g++.dg/cpp2a/range-for18.C
> index e69de29bb2d..e38b41cc30d 100644
> --- gcc/testsuite/g++.dg/cpp2a/range-for18.C
> +++ gcc/testsuite/g++.dg/cpp2a/range-for18.C
> @@ -0,0 +1,16 @@
> +// PR c++/87152
> +// { dg-do compile }
> +// { dg-options "-std=c++2a" }
> +
> +template<int> void foo()
> +{
> +  int a[] = { 1, 1, 1 };
> +  for (int i = 0; auto x : a);
> +  int i;
> +}
> +
> +void
> +bar ()
> +{
> +  foo<0>();
> +}
> diff --git gcc/testsuite/g++.dg/parse/error61.C gcc/testsuite/g++.dg/parse/error61.C
> index 199e1aa721c..272626bd734 100644
> --- gcc/testsuite/g++.dg/parse/error61.C
> +++ gcc/testsuite/g++.dg/parse/error61.C
> @@ -4,8 +4,8 @@
>  template<int> void foo()
>  {
>    int x[8];
> -  for (int& i, j : x) // { dg-error "multiple" }
> -    i = 0; // { dg-error "local variable" }
> +  for (int& i, j : x) // { dg-error "multiple|reference" }
> +    i = 0;
>  }
>
>  void bar()
diff mbox series

Patch

diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index f646519135f..6c2689064f3 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -5767,6 +5767,8 @@  potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
       return true;
 
     case RANGE_FOR_STMT:
+      if (!RECUR (RANGE_FOR_INIT_STMT (t), any))
+	return false;
       if (!RECUR (RANGE_FOR_EXPR (t), any))
 	return false;
       if (!RECUR (RANGE_FOR_BODY (t), any))
diff --git gcc/cp/cp-tree.def gcc/cp/cp-tree.def
index 1b0326f4e81..c64225ded6f 100644
--- gcc/cp/cp-tree.def
+++ gcc/cp/cp-tree.def
@@ -301,9 +301,10 @@  DEFTREECODE (IF_STMT, "if_stmt", tcc_statement, 4)
 DEFTREECODE (FOR_STMT, "for_stmt", tcc_statement, 5)
 
 /* Used to represent a range-based `for' statement. The operands are
-   RANGE_FOR_DECL, RANGE_FOR_EXPR, RANGE_FOR_BODY, and RANGE_FOR_SCOPE,
-   RANGE_FOR_UNROLL respectively.  Only used in templates.  */
-DEFTREECODE (RANGE_FOR_STMT, "range_for_stmt", tcc_statement, 5)
+   RANGE_FOR_DECL, RANGE_FOR_EXPR, RANGE_FOR_BODY, RANGE_FOR_SCOPE,
+   RANGE_FOR_UNROLL, and RANGE_FOR_INIT_STMT, respectively.  Only used in
+   templates.  */
+DEFTREECODE (RANGE_FOR_STMT, "range_for_stmt", tcc_statement, 6)
 
 /* Used to represent a 'while' statement. The operands are WHILE_COND
    and WHILE_BODY, respectively.  */
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index df441fca304..b78e9eb252b 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -4923,6 +4923,7 @@  more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define RANGE_FOR_BODY(NODE)	TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 2)
 #define RANGE_FOR_SCOPE(NODE)	TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 3)
 #define RANGE_FOR_UNROLL(NODE)	TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 4)
+#define RANGE_FOR_INIT_STMT(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 5)
 #define RANGE_FOR_IVDEP(NODE)	TREE_LANG_FLAG_6 (RANGE_FOR_STMT_CHECK (NODE))
 
 #define SWITCH_STMT_COND(NODE)	TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 0)
diff --git gcc/cp/dump.c gcc/cp/dump.c
index 9c1e5fc781a..d9b868bfaef 100644
--- gcc/cp/dump.c
+++ gcc/cp/dump.c
@@ -301,6 +301,7 @@  cp_dump_tree (void* dump_info, tree t)
 
     case RANGE_FOR_STMT:
       dump_stmt (di, t);
+      dump_child ("init", RANGE_FOR_INIT_STMT (t));
       dump_child ("decl", RANGE_FOR_DECL (t));
       dump_child ("expr", RANGE_FOR_EXPR (t));
       dump_child ("body", RANGE_FOR_BODY (t));
diff --git gcc/cp/pt.c gcc/cp/pt.c
index 0a618a5447d..892a387cbc5 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -16815,6 +16815,7 @@  tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
         stmt = (processing_template_decl
 		? begin_range_for_stmt (NULL_TREE, NULL_TREE)
 		: begin_for_stmt (NULL_TREE, NULL_TREE));
+	RECUR (RANGE_FOR_INIT_STMT (t));
         decl = RANGE_FOR_DECL (t);
         decl = tsubst (decl, args, complain, in_decl);
         maybe_push_decl (decl);
diff --git gcc/cp/semantics.c gcc/cp/semantics.c
index 676de011868..f3e5d83b1ef 100644
--- gcc/cp/semantics.c
+++ gcc/cp/semantics.c
@@ -1101,8 +1101,8 @@  begin_range_for_stmt (tree scope, tree init)
 {
   begin_maybe_infinite_loop (boolean_false_node);
 
-  tree r = build_stmt (input_location, RANGE_FOR_STMT,
-		       NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
+  tree r = build_stmt (input_location, RANGE_FOR_STMT, NULL_TREE, NULL_TREE,
+		       NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
 
   if (scope == NULL_TREE)
     {
@@ -1110,22 +1110,23 @@  begin_range_for_stmt (tree scope, tree init)
       scope = begin_for_scope (&init);
     }
 
-  /* RANGE_FOR_STMTs do not use nor save the init tree, so we
-     pop it now.  */
-  if (init)
-    pop_stmt_list (init);
+  /* Since C++20, RANGE_FOR_STMTs can use the init tree, so save it.  */
+  RANGE_FOR_INIT_STMT (r) = init;
   RANGE_FOR_SCOPE (r) = scope;
 
   return r;
 }
 
 /* Finish the head of a range-based for statement, which may
-   be given by RANGE_FOR_STMT. DECL must be the declaration
+   be given by RANGE_FOR_STMT.  DECL must be the declaration
    and EXPR must be the loop expression. */
 
 void
 finish_range_for_decl (tree range_for_stmt, tree decl, tree expr)
 {
+  if (processing_template_decl)
+    RANGE_FOR_INIT_STMT (range_for_stmt)
+      = pop_stmt_list (RANGE_FOR_INIT_STMT (range_for_stmt));
   RANGE_FOR_DECL (range_for_stmt) = decl;
   RANGE_FOR_EXPR (range_for_stmt) = expr;
   add_stmt (range_for_stmt);
diff --git gcc/testsuite/g++.dg/cpp2a/range-for11.C gcc/testsuite/g++.dg/cpp2a/range-for11.C
index e69de29bb2d..5f0a80318ab 100644
--- gcc/testsuite/g++.dg/cpp2a/range-for11.C
+++ gcc/testsuite/g++.dg/cpp2a/range-for11.C
@@ -0,0 +1,22 @@ 
+// PR c++/87152
+// { dg-do run }
+// { dg-options "-std=c++2a" }
+
+template<typename>
+int foo ()
+{
+  int a[] = { 1, 2, 3, 4, 5 };
+  int j = 0;
+  for (int i = 0; auto x : a)
+    j += i++;
+
+  return j;
+}
+
+int
+main ()
+{
+  int j = foo<int>();
+  if (j != 10)
+    __builtin_abort ();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/range-for12.C gcc/testsuite/g++.dg/cpp2a/range-for12.C
index e69de29bb2d..09558341bb9 100644
--- gcc/testsuite/g++.dg/cpp2a/range-for12.C
+++ gcc/testsuite/g++.dg/cpp2a/range-for12.C
@@ -0,0 +1,33 @@ 
+// PR c++/87152
+// { dg-do compile }
+// { dg-options "-std=c++2a" }
+
+static const int a[] = { 1, 2, 3, 4, 5 };
+extern void foo (int);
+extern void bar (int, int);
+
+constexpr int
+baz ()
+{
+  return 6;
+}
+
+template<typename T>
+void
+fn1 (T i)
+{
+  for ((i += 2); auto x : a)
+    foo (i);
+
+  for (auto j = 0, k = 0; auto x : a)
+    bar (j + k, x);
+
+  for (constexpr int j = baz (); auto x : a)
+    bar (x, j);
+}
+
+void
+do_fn1 ()
+{
+  fn1<int>(10);
+}
diff --git gcc/testsuite/g++.dg/cpp2a/range-for13.C gcc/testsuite/g++.dg/cpp2a/range-for13.C
index e69de29bb2d..fb1ff285529 100644
--- gcc/testsuite/g++.dg/cpp2a/range-for13.C
+++ gcc/testsuite/g++.dg/cpp2a/range-for13.C
@@ -0,0 +1,33 @@ 
+// PR c++/87152
+// { dg-do run }
+// { dg-options "-std=c++2a" }
+
+template<typename T>
+void foo ()
+{
+  int a[] = { 1, 2, 3, 4, 5 };
+
+  for (T i = 1; auto x : a)
+    if (i++ != x)
+      __builtin_abort ();
+
+  T i;
+  for (i = 1; auto x : a)
+    if (i++ != x)
+      __builtin_abort ();
+
+  i = 0;
+  for (i++; auto x : a)
+    if (i != 1)
+      __builtin_abort ();
+
+  for (T s[] = { 1, 1, 1 }; auto x : s)
+    if (x != 1)
+      __builtin_abort ();
+}
+
+int
+main ()
+{
+  foo<int>();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/range-for14.C gcc/testsuite/g++.dg/cpp2a/range-for14.C
index e69de29bb2d..94ff3c19153 100644
--- gcc/testsuite/g++.dg/cpp2a/range-for14.C
+++ gcc/testsuite/g++.dg/cpp2a/range-for14.C
@@ -0,0 +1,24 @@ 
+// PR c++/87152
+// { dg-do run }
+// { dg-options "-std=c++2a" }
+
+template<typename T>
+void
+fn ()
+{
+  T a[] = { 1, 2, 3, 4, 5 };
+
+  for (T i = []{ return 3; }(); auto x : a)
+    if (i != 3)
+      __builtin_abort ();
+
+  for (T i = ({ 3; }); auto x : a)
+    if (i != 3)
+      __builtin_abort ();
+}
+
+int
+main ()
+{
+  fn<int>();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/range-for15.C gcc/testsuite/g++.dg/cpp2a/range-for15.C
index e69de29bb2d..532b7689459 100644
--- gcc/testsuite/g++.dg/cpp2a/range-for15.C
+++ gcc/testsuite/g++.dg/cpp2a/range-for15.C
@@ -0,0 +1,43 @@ 
+// PR c++/87152
+// { dg-do run }
+// { dg-options "-std=c++2a" }
+
+struct A { int i; long long j; } a[64];
+
+template<typename T>
+void foo ()
+{
+  for (T i = 0; auto &x : a)
+    {
+      x.i = i;
+      x.j = 2 * i++;
+    }
+  for (auto & [ x, y ] : a)
+    {
+      x += 2;
+      y += 3;
+    }
+  for (T i = 0; const auto [ u, v ] : a)
+    {
+      if (u != i + 2 || v != 2 * i++ + 3)
+        __builtin_abort ();
+    }
+  for (T i = 0; auto [ x, y ] : a)
+    {
+      x += 4;
+      y += 5;
+      if (x != i + 6 || y != 2 * i++ + 8)
+        __builtin_abort ();
+    }
+  for (T i = 0; const auto x : a)
+    {
+      if (x.i != i + 2 || x.j != 2 * i++ + 3)
+        __builtin_abort ();
+    }
+}
+
+int
+main ()
+{
+  foo<int>();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/range-for16.C gcc/testsuite/g++.dg/cpp2a/range-for16.C
index e69de29bb2d..cbfd9873387 100644
--- gcc/testsuite/g++.dg/cpp2a/range-for16.C
+++ gcc/testsuite/g++.dg/cpp2a/range-for16.C
@@ -0,0 +1,36 @@ 
+// PR c++/87152
+// { dg-do run }
+// { dg-options "-std=c++2a" }
+
+struct A { int i, j; };
+
+template<typename T>
+void foo ()
+{
+  A a = { .i = 2, .j = 3 };
+  T arr[] = { 1, 1, 1 };
+
+  for (auto & [ x, y ] = a; auto z : arr)
+    if (x + z != 3 || y + z != 4)
+      __builtin_abort ();
+
+  for (T d = 1; auto &z : arr)
+    z += d;
+
+  for (const auto [ x, y ] = a; auto z : arr)
+    if (x + z != 4 || y + z != 5)
+      __builtin_abort ();
+
+  for (T d = 1; auto &z : arr)
+    z += d;
+
+  for (auto [ x, y ] = a; auto z : arr)
+    if (x + z != 5 || y + z != 6)
+      __builtin_abort ();
+}
+
+int
+main ()
+{
+  foo<int>();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/range-for17.C gcc/testsuite/g++.dg/cpp2a/range-for17.C
index e69de29bb2d..2e8734b795d 100644
--- gcc/testsuite/g++.dg/cpp2a/range-for17.C
+++ gcc/testsuite/g++.dg/cpp2a/range-for17.C
@@ -0,0 +1,30 @@ 
+// PR c++/87152
+// { dg-do run }
+// { dg-options "-std=c++2a" }
+
+struct A { int i; long long j; } a[64];
+
+template<typename>
+void foo ()
+{
+  A b = { 1, 2 };
+  for (auto & [ u, v ] : a)
+    {
+      u = 2;
+      v = 3;
+    }
+
+  for (auto [x, y] = b; auto [ u, v ] : a)
+    if (y + u != x + v)
+      __builtin_abort ();
+
+  for (auto [x, y] = b; auto & [ u, v ] : a)
+    if (y + u != x + v)
+      __builtin_abort ();
+}
+
+int
+main ()
+{
+  foo<int>();
+}
diff --git gcc/testsuite/g++.dg/cpp2a/range-for18.C gcc/testsuite/g++.dg/cpp2a/range-for18.C
index e69de29bb2d..e38b41cc30d 100644
--- gcc/testsuite/g++.dg/cpp2a/range-for18.C
+++ gcc/testsuite/g++.dg/cpp2a/range-for18.C
@@ -0,0 +1,16 @@ 
+// PR c++/87152
+// { dg-do compile }
+// { dg-options "-std=c++2a" }
+
+template<int> void foo()
+{
+  int a[] = { 1, 1, 1 };
+  for (int i = 0; auto x : a);
+  int i;
+}
+
+void
+bar ()
+{
+  foo<0>();
+}
diff --git gcc/testsuite/g++.dg/parse/error61.C gcc/testsuite/g++.dg/parse/error61.C
index 199e1aa721c..272626bd734 100644
--- gcc/testsuite/g++.dg/parse/error61.C
+++ gcc/testsuite/g++.dg/parse/error61.C
@@ -4,8 +4,8 @@ 
 template<int> void foo()
 {
   int x[8];
-  for (int& i, j : x) // { dg-error "multiple" }
-    i = 0; // { dg-error "local variable" }
+  for (int& i, j : x) // { dg-error "multiple|reference" }
+    i = 0;
 }
 
 void bar()