https://gcc.gnu.org/g:24cd4753e470ec122d232c9da825274bce0bae15

commit r14-11787-g24cd4753e470ec122d232c9da825274bce0bae15
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Fri Feb 7 17:07:23 2025 +0100

    c++: Fix up handling of for/while loops with declarations in condition 
[PR86769]
    
    As the following testcase show (note, just for-{3,4,6,7,8}.C, 
constexpr-86769.C
    and stmtexpr27.C FAIL without the patch, the rest is just that I couldn't
    find coverage for some details and so added tests we don't regress or for5.C
    is from Marek's attempt in the PR), we weren't correctly handling for/while
    loops with declarations as conditions.
    
    The C++ FE has the simplify_loop_decl_cond function which transforms
    such loops as mentioned in the comment:
                while (A x = 42) { }
                for (; A x = 42;) { }
       becomes
                while (true) { A x = 42; if (!x) break; }
                for (;;) { A x = 42; if (!x) break; }
    For for loops this is not enough, as the x declaration should be
    still in scope when expr (if any) is executed, and injecting the
    expr expression into the body in the FE needs to have the continue
    label in between, something normally added by the c-family
    genericization.  One of my thoughts was to just add there an artificial
    label plus the expr expression in the FE and tell c-family about that
    label, so that it doesn't create it but uses what has been emitted.
    
    Unfortunately break/continue are resolved to labels only at c-family
    genericization time and by moving the condition (and its preparation
    statements such as the DECL_EXPR) into the body (and perhaps by also
    moving there the (increment) expr as well) we resolve incorrectly any
    break/continue statement appearing in cond (or newly perhaps also expr)
    expression(s).  While in standard C++ one can't have something like that
    there, with statement expressions they are possible there, and we actually
    have testsuite coverage that when they appear outside of the body of the
    loop they bind to an outer loop rather than the inner one.  When the FE
    moves everything into the body, c-family can't distinguish any more between
    the user body vs. the condition/preparation statements vs. expr expression.
    
    So, the following patch instead keeps them separate and does the merging
    only at the c-family loop genericization time.  For that the patch
    introduces two new operands of FOR_STMT and WHILE_STMT, *_COND_PREP
    which is forced to be a BIND_EXPR which contains the preparation statements
    like DECL_EXPR, and the initialization of that variable, so basically what
    {FOR,WHILE}_BODY has when we encounter the function dealing with this,
    except one optional CLEANUP_STMT at the end which holds cleanups for the
    variable if it needs to be destructed.  This CLEANUP_STMT is removed and
    the actual cleanup saved into another new operand, *_COND_CLEANUP.
    
    The c-family loop genericization handles such loops roughly the way
    https://eel.is/c++draft/stmt.for and https://eel.is/c++draft/stmt.while
    specifies, so the body is (if *_COND_CLEANUP is non-NULL)
    { A x = 42; try { if (!x) break; body; cont_label: expr; } finally { 
cleanup; } }
    and otherwise
    { A x = 42; if (!x) break; body; cont_label: expr; }
    i.e. the *_COND, *_BODY, optional continue label, FOR_EXPR  are appended
    into the body of the *_COND_PREP BIND_EXPR.
    
    And when doing constexpr evaluation of such FOR/WHILE loops, we treat
    it similarly, first evaluate *_COND_PREP except the
          for (tree decl = BIND_EXPR_VARS (t); decl; decl = DECL_CHAIN (decl))
            destroy_value_checked (ctx, decl, non_constant_p);
    part of BIND_EXPR handling for it, then evaluate *_COND (and decide based
    on whether it was false or true like before), then *_BODY, then FOR_EXPR,
    then *_COND_CLEANUP (similarly to the way how CLEANUP_STMT handling handles
    that) and finally do those destroy_value_checked.
    
    Note, the constexpr-86769.C testcase FAILs with both clang++ and MSVC (note,
    the rest of tests PASS with clang++) but I believe it must be just a bug
    in those compilers, new int is done in all the constructors and delete is
    done in the destructor, so when clang++ reports one of the new int weren't
    deallocated during constexpr evaluation I don't see how that would be
    possible.  When the same test has all the constexpr stuff, all the new int
    are properly deleted at runtime when compiled by both compilers and valgrind
    is happy about it, no leaks.
    
    2025-02-07  Jakub Jelinek  <ja...@redhat.com>
                Jason Merrill  <ja...@redhat.com>
    
            PR c++/86769
    gcc/c-family/
            * c-common.def (FOR_STMT): Add 2 operands and document them.
            (WHILE_STMT): Likewise.
            * c-common.h (WHILE_COND_PREP, WHILE_COND_CLEANUP): Define.
            (FOR_COND_PREP, FOR_COND_CLEANUP): Define.
            * c-gimplify.cc (genericize_c_loop): Add COND_PREP and COND_CLEANUP
            arguments, handle them if they are non-NULL.
            (genericize_for_stmt, genericize_while_stmt, genericize_do_stmt):
            Adjust callers.
    gcc/c/
            * c-parser.cc (c_parser_while_statement): Add 2 further NULL_TREE
            operands to build_stmt.
            (c_parser_for_statement): Likewise.
    gcc/cp/
            * semantics.cc (set_one_cleanup_loc): New function.
            (set_cleanup_locs): Use it.
            (simplify_loop_decl_cond): Remove.
            (adjust_loop_decl_cond): New function.
            (begin_while_stmt): Add 2 further NULL_TREE operands to build_stmt.
            (finish_while_stmt_cond): Call adjust_loop_decl_cond instead of
            simplify_loop_decl_cond.
            (finish_while_stmt): Call do_poplevel also on WHILE_COND_PREP if
            non-NULL and also use pop_stmt_list rather than do_poplevel for
            WHILE_BODY in that case.  Call set_one_cleanup_loc.
            (begin_for_stmt): Add 2 further NULL_TREE operands to build_stmt.
            (finish_for_cond): Call adjust_loop_decl_cond instead of
            simplify_loop_decl_cond.
            (finish_for_stmt): Call do_poplevel also on FOR_COND_PREP if 
non-NULL
            and also use pop_stmt_list rather than do_poplevel for FOR_BODY in
            that case.  Call set_one_cleanup_loc.
            * constexpr.cc (cxx_eval_loop_expr): Handle
            {WHILE,FOR}_COND_{PREP,CLEANUP}.
            (check_for_return_continue): Handle {WHILE,FOR}_COND_PREP.
            (potential_constant_expression_1): RECUR on
            {WHILE,FOR}_COND_{PREP,CLEANUP}.
    gcc/testsuite/
            * g++.dg/diagnostic/redeclaration-7.C: New test.
            * g++.dg/expr/for3.C: New test.
            * g++.dg/expr/for4.C: New test.
            * g++.dg/expr/for5.C: New test.
            * g++.dg/expr/for6.C: New test.
            * g++.dg/expr/for7.C: New test.
            * g++.dg/expr/for8.C: New test.
            * g++.dg/ext/stmtexpr27.C: New test.
            * g++.dg/cpp2a/constexpr-86769.C: New test.
            * g++.dg/cpp26/name-independent-decl7.C: New test.
            * g++.dg/cpp26/name-independent-decl8.C: New test.
    
    (cherry picked from commit 35d40b56eb6e7ac63c790a799d3b367742d58a5e)

Diff:
---
 gcc/c-family/c-common.def                          |  13 +--
 gcc/c-family/c-common.h                            |  13 ++-
 gcc/c-family/c-gimplify.cc                         |  96 ++++++++++++++---
 gcc/c/c-parser.cc                                  |   5 +-
 gcc/cp/constexpr.cc                                |  40 +++++++
 gcc/cp/semantics.cc                                | 117 +++++++++++++--------
 .../g++.dg/cpp26/name-independent-decl7.C          |  27 +++++
 .../g++.dg/cpp26/name-independent-decl8.C          |  24 +++++
 gcc/testsuite/g++.dg/cpp2a/constexpr-86769.C       |  36 +++++++
 gcc/testsuite/g++.dg/diagnostic/redeclaration-7.C  |  23 ++++
 gcc/testsuite/g++.dg/expr/for3.C                   | 112 ++++++++++++++++++++
 gcc/testsuite/g++.dg/expr/for4.C                   | 116 ++++++++++++++++++++
 gcc/testsuite/g++.dg/expr/for5.C                   |  34 ++++++
 gcc/testsuite/g++.dg/expr/for6.C                   |  39 +++++++
 gcc/testsuite/g++.dg/expr/for7.C                   |  20 ++++
 gcc/testsuite/g++.dg/expr/for8.C                   |  22 ++++
 gcc/testsuite/g++.dg/ext/stmtexpr27.C              |  64 +++++++++++
 17 files changed, 736 insertions(+), 65 deletions(-)

diff --git a/gcc/c-family/c-common.def b/gcc/c-family/c-common.def
index 5de96e5d4a89..2c22352ad0e3 100644
--- a/gcc/c-family/c-common.def
+++ b/gcc/c-family/c-common.def
@@ -58,13 +58,14 @@ DEFTREECODE (SIZEOF_EXPR, "sizeof_expr", tcc_expression, 1)
 DEFTREECODE (PAREN_SIZEOF_EXPR, "paren_sizeof_expr", tcc_expression, 1)
 
 /* Used to represent a `for' statement. The operands are
-   FOR_INIT_STMT, FOR_COND, FOR_EXPR, FOR_BODY, and FOR_SCOPE,
-   respectively.  */
-DEFTREECODE (FOR_STMT, "for_stmt", tcc_statement, 5)
+   FOR_INIT_STMT, FOR_COND, FOR_EXPR, FOR_BODY, FOR_SCOPE,
+   FOR_COND_PREP and FOR_COND_CLEANUP, respectively.  */
+DEFTREECODE (FOR_STMT, "for_stmt", tcc_statement, 7)
 
-/* Used to represent a 'while' statement. The operands are WHILE_COND
-   and WHILE_BODY, respectively.  */
-DEFTREECODE (WHILE_STMT, "while_stmt", tcc_statement, 2)
+/* Used to represent a 'while' statement. The operands are WHILE_COND,
+   WHILE_BODY, WHILE_COND_PREP and WHILE_COND_CLEANUP,
+   respectively.  */
+DEFTREECODE (WHILE_STMT, "while_stmt", tcc_statement, 4)
 
 /* Used to represent a 'do' statement. The operands are DO_COND and
    DO_BODY, respectively.  */
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 2d5f53998855..41f7ca3ef012 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1490,24 +1490,29 @@ extern tree build_userdef_literal (tree suffix_id, tree 
value,
                                   tree num_string);
 
 
-/* WHILE_STMT accessors. These give access to the condition of the
-   while statement and the body of the while statement, respectively.  */
+/* WHILE_STMT accessors.  These give access to the condition of the
+   while statement and the body of the while statement, and
+   condition preparation statements and its cleanup, respectively.  */
 #define WHILE_COND(NODE)       TREE_OPERAND (WHILE_STMT_CHECK (NODE), 0)
 #define WHILE_BODY(NODE)       TREE_OPERAND (WHILE_STMT_CHECK (NODE), 1)
+#define WHILE_COND_PREP(NODE)  TREE_OPERAND (WHILE_STMT_CHECK (NODE), 2)
+#define WHILE_COND_CLEANUP(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 3)
 
 /* DO_STMT accessors. These give access to the condition of the do
    statement and the body of the do statement, respectively.  */
 #define DO_COND(NODE)          TREE_OPERAND (DO_STMT_CHECK (NODE), 0)
 #define DO_BODY(NODE)          TREE_OPERAND (DO_STMT_CHECK (NODE), 1)
 
-/* FOR_STMT accessors. These give access to the init statement,
+/* FOR_STMT accessors.  These give access to the init statement,
    condition, update expression, and body of the for statement,
-   respectively.  */
+   and condition preparation statements and its cleanup, respectively.  */
 #define FOR_INIT_STMT(NODE)    TREE_OPERAND (FOR_STMT_CHECK (NODE), 0)
 #define FOR_COND(NODE)         TREE_OPERAND (FOR_STMT_CHECK (NODE), 1)
 #define FOR_EXPR(NODE)         TREE_OPERAND (FOR_STMT_CHECK (NODE), 2)
 #define FOR_BODY(NODE)         TREE_OPERAND (FOR_STMT_CHECK (NODE), 3)
 #define FOR_SCOPE(NODE)                TREE_OPERAND (FOR_STMT_CHECK (NODE), 4)
+#define FOR_COND_PREP(NODE)    TREE_OPERAND (FOR_STMT_CHECK (NODE), 5)
+#define FOR_COND_CLEANUP(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 6)
 
 #define SWITCH_STMT_COND(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 0)
 #define SWITCH_STMT_BODY(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 1)
diff --git a/gcc/c-family/c-gimplify.cc b/gcc/c-family/c-gimplify.cc
index 494da49791d5..3fb2aa123cae 100644
--- a/gcc/c-family/c-gimplify.cc
+++ b/gcc/c-family/c-gimplify.cc
@@ -225,23 +225,31 @@ expr_loc_or_loc (const_tree expr, location_t or_loc)
    controlled by the loop.  INCR is the increment expression of a for-loop,
    or NULL_TREE.  COND_IS_FIRST indicates whether the condition is
    evaluated before the loop body as in while and for loops, or after the
-   loop body as in do-while loops.  */
+   loop body as in do-while loops.  COND_PREP and COND_CLEANUP are used
+   for C++ for/while loops with variable declaration as condition.  COND_PREP
+   is a BIND_EXPR with the declaration and initialization of the condition
+   variable, into which COND, BODY, continue label if needed and INCR if
+   non-NULL should be appended, and COND_CLEANUP are statements which should
+   be evaluated after that or if anything in COND, BODY or INCR throws.  */
 
 static void
 genericize_c_loop (tree *stmt_p, location_t start_locus, tree cond, tree body,
-                  tree incr, bool cond_is_first, int *walk_subtrees,
-                  void *data, walk_tree_fn func, walk_tree_lh lh)
+                  tree incr, tree cond_prep, tree cond_cleanup,
+                  bool cond_is_first, int *walk_subtrees, void *data,
+                  walk_tree_fn func, walk_tree_lh lh)
 {
   tree blab, clab;
   tree entry = NULL, exit = NULL, t;
-  tree stmt_list = NULL;
+  tree stmt_list = NULL, outer_stmt_list = NULL_TREE, *stmt_list_p = NULL;
   location_t cond_locus = expr_loc_or_loc (cond, start_locus);
   location_t incr_locus = expr_loc_or_loc (incr, start_locus);
 
   protected_set_expr_location_if_unset (incr, start_locus);
 
+  walk_tree_1 (&cond_prep, func, data, NULL, lh);
   walk_tree_1 (&cond, func, data, NULL, lh);
   walk_tree_1 (&incr, func, data, NULL, lh);
+  walk_tree_1 (&cond_cleanup, func, data, NULL, lh);
 
   blab = begin_bc_block (bc_break, start_locus);
   clab = begin_bc_block (bc_continue, start_locus);
@@ -249,9 +257,66 @@ genericize_c_loop (tree *stmt_p, location_t start_locus, 
tree cond, tree body,
   walk_tree_1 (&body, func, data, NULL, lh);
   *walk_subtrees = 0;
 
-  /* If condition is zero don't generate a loop construct.  */
-  if (cond && integer_zerop (cond))
+  if (cond_prep)
     {
+      /* The C++ cases of
+        while (A x = 42) body;
+        for (; A x = 42; expr) body;
+        This should be expanded into:
+
+        top:
+        COND_PREP
+
+        with either
+
+        if (COND); else break;
+        BODY;
+        cont:
+        EXPR;
+        goto top;
+
+        or
+
+        try {
+          if (COND); else break;
+          BODY;
+          cont:
+          EXPR;
+        } finally {
+          COND_CLEANUP
+        }
+
+        appended into COND_PREP body.  */
+      gcc_assert (cond_is_first && TREE_CODE (cond_prep) == BIND_EXPR);
+      tree top = build1 (LABEL_EXPR, void_type_node,
+                        create_artificial_label (start_locus));
+      exit = build1 (GOTO_EXPR, void_type_node, LABEL_EXPR_LABEL (top));
+      append_to_statement_list (top, &outer_stmt_list);
+      append_to_statement_list (cond_prep, &outer_stmt_list);
+      stmt_list = BIND_EXPR_BODY (cond_prep);
+      BIND_EXPR_BODY (cond_prep) = NULL_TREE;
+      stmt_list_p = &BIND_EXPR_BODY (cond_prep);
+      if (cond_cleanup && TREE_SIDE_EFFECTS (cond_cleanup))
+       {
+         t = build2_loc (EXPR_LOCATION (cond_cleanup), TRY_FINALLY_EXPR,
+                         void_type_node, NULL_TREE, cond_cleanup);
+         append_to_statement_list (t, &stmt_list);
+         *stmt_list_p = stmt_list;
+         stmt_list_p = &TREE_OPERAND (t, 0);
+         stmt_list = NULL_TREE;
+       }
+      tree after_cond = create_artificial_label (cond_locus);
+      tree goto_after_cond = build1 (GOTO_EXPR, void_type_node, after_cond);
+      t = build1 (GOTO_EXPR, void_type_node, get_bc_label (bc_break));
+      t = fold_build3_loc (cond_locus, COND_EXPR, void_type_node, cond,
+                          goto_after_cond, t);
+      append_to_statement_list (t, &stmt_list);
+      t = build1 (LABEL_EXPR, void_type_node, after_cond);
+      append_to_statement_list (t, &stmt_list);
+    }
+  else if (cond && integer_zerop (cond))
+    {
+      /* If condition is zero don't generate a loop construct.  */
       if (cond_is_first)
        {
          t = build1_loc (start_locus, GOTO_EXPR, void_type_node,
@@ -348,6 +413,11 @@ genericize_c_loop (tree *stmt_p, location_t start_locus, 
tree cond, tree body,
       append_to_statement_list (d, &stmt_list);
     }
   append_to_statement_list (exit, &stmt_list);
+  if (stmt_list_p)
+    {
+      *stmt_list_p = stmt_list;
+      stmt_list = outer_stmt_list;
+    }
   finish_bc_block (&stmt_list, bc_break, blab);
   if (!stmt_list)
     stmt_list = build_empty_stmt (start_locus);
@@ -373,8 +443,9 @@ genericize_for_stmt (tree *stmt_p, int *walk_subtrees, void 
*data,
     }
 
   genericize_c_loop (&loop, EXPR_LOCATION (stmt), FOR_COND (stmt),
-                    FOR_BODY (stmt), FOR_EXPR (stmt), 1, walk_subtrees,
-                    data, func, lh);
+                    FOR_BODY (stmt), FOR_EXPR (stmt),
+                    FOR_COND_PREP (stmt), FOR_COND_CLEANUP (stmt), 1,
+                    walk_subtrees, data, func, lh);
   append_to_statement_list (loop, &expr);
   if (expr == NULL_TREE)
     expr = loop;
@@ -389,8 +460,9 @@ genericize_while_stmt (tree *stmt_p, int *walk_subtrees, 
void *data,
 {
   tree stmt = *stmt_p;
   genericize_c_loop (stmt_p, EXPR_LOCATION (stmt), WHILE_COND (stmt),
-                    WHILE_BODY (stmt), NULL_TREE, 1, walk_subtrees,
-                    data, func, lh);
+                    WHILE_BODY (stmt), NULL_TREE,
+                    WHILE_COND_PREP (stmt), WHILE_COND_CLEANUP (stmt), 1,
+                    walk_subtrees, data, func, lh);
 }
 
 /* Genericize a DO_STMT node *STMT_P.  */
@@ -401,8 +473,8 @@ genericize_do_stmt (tree *stmt_p, int *walk_subtrees, void 
*data,
 {
   tree stmt = *stmt_p;
   genericize_c_loop (stmt_p, EXPR_LOCATION (stmt), DO_COND (stmt),
-                    DO_BODY (stmt), NULL_TREE, 0, walk_subtrees,
-                    data, func, lh);
+                    DO_BODY (stmt), NULL_TREE,
+                    NULL_TREE, NULL_TREE, 0, walk_subtrees, data, func, lh);
 }
 
 /* Genericize a SWITCH_STMT node *STMT_P by turning it into a SWITCH_EXPR.  */
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 686ab423d033..dccceb7a4787 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -8149,7 +8149,8 @@ c_parser_while_statement (c_parser *parser, bool ivdep, 
unsigned short unroll,
   location_t loc_after_labels;
   bool open_brace = c_parser_next_token_is (parser, CPP_OPEN_BRACE);
   body = c_parser_c99_block_statement (parser, if_p, &loc_after_labels);
-  add_stmt (build_stmt (loc, WHILE_STMT, cond, body));
+  add_stmt (build_stmt (loc, WHILE_STMT, cond, body, NULL_TREE,
+                       NULL_TREE));
   add_stmt (c_end_compound_stmt (loc, block, flag_isoc99));
   c_parser_maybe_reclassify_token (parser);
 
@@ -8507,7 +8508,7 @@ c_parser_for_statement (c_parser *parser, bool ivdep, 
unsigned short unroll,
                              objc_foreach_continue_label);
   else
     add_stmt (build_stmt (for_loc, FOR_STMT, NULL_TREE, cond, incr,
-                         body, NULL_TREE));
+                         body, NULL_TREE, NULL_TREE, NULL_TREE));
   add_stmt (c_end_compound_stmt (for_loc, block,
                                 flag_isoc99 || c_dialect_objc ()));
   c_parser_maybe_reclassify_token (parser);
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index d487a16645d5..67e76367b9eb 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -7089,6 +7089,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
     }
 
   tree body, cond = NULL_TREE, expr = NULL_TREE;
+  tree cond_prep = NULL_TREE, cond_cleanup = NULL_TREE;
   int count = 0;
   switch (TREE_CODE (t))
     {
@@ -7102,6 +7103,8 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
     case WHILE_STMT:
       body = WHILE_BODY (t);
       cond = WHILE_COND (t);
+      cond_prep = WHILE_COND_PREP (t);
+      cond_cleanup = WHILE_COND_CLEANUP (t);
       count = -1;
       break;
     case FOR_STMT:
@@ -7113,11 +7116,25 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
       body = FOR_BODY (t);
       cond = FOR_COND (t);
       expr = FOR_EXPR (t);
+      cond_prep = FOR_COND_PREP (t);
+      cond_cleanup = FOR_COND_CLEANUP (t);
       count = -1;
       break;
     default:
       gcc_unreachable ();
     }
+  if (cond_prep)
+    gcc_assert (TREE_CODE (cond_prep) == BIND_EXPR);
+  auto cleanup_cond = [=] {
+    /* Clean up the condition variable after each iteration.  */
+    if (cond_cleanup && !*non_constant_p)
+      cxx_eval_constant_expression (ctx, cond_cleanup, vc_discard,
+                                   non_constant_p, overflow_p);
+    if (cond_prep)
+      for (tree decl = BIND_EXPR_VARS (cond_prep);
+          decl; decl = DECL_CHAIN (decl))
+       destroy_value_checked (ctx, decl, non_constant_p);
+  };
   do
     {
       if (count != -1)
@@ -7139,6 +7156,17 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
            cxx_eval_constant_expression (ctx, expr, vc_prvalue,
                                          non_constant_p, overflow_p,
                                          jump_target);
+         cleanup_cond ();
+       }
+
+      if (cond_prep)
+       {
+         for (tree decl = BIND_EXPR_VARS (cond_prep);
+              decl; decl = DECL_CHAIN (decl))
+           ctx->global->clear_value (decl);
+         cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (cond_prep),
+                                       vc_discard, non_constant_p,
+                                       overflow_p, jump_target);
        }
 
       if (cond)
@@ -7176,6 +7204,8 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
         && (!switches (jump_target) || count == 0)
         && !*non_constant_p);
 
+  cleanup_cond ();
+
   return NULL_TREE;
 }
 
@@ -9571,6 +9601,7 @@ check_for_return_continue (tree *tp, int *walk_subtrees, 
void *data)
 
     case WHILE_STMT:
       *walk_subtrees = 0;
+      RECUR (WHILE_COND_PREP (t));
       RECUR (WHILE_COND (t));
       s = d->continue_stmt;
       b = d->break_stmt;
@@ -9582,6 +9613,7 @@ check_for_return_continue (tree *tp, int *walk_subtrees, 
void *data)
     case FOR_STMT:
       *walk_subtrees = 0;
       RECUR (FOR_INIT_STMT (t));
+      RECUR (FOR_COND_PREP (t));
       RECUR (FOR_COND (t));
       RECUR (FOR_EXPR (t));
       s = d->continue_stmt;
@@ -10083,6 +10115,8 @@ potential_constant_expression_1 (tree t, bool 
want_rval, bool strict, bool now,
     case FOR_STMT:
       if (!RECUR (FOR_INIT_STMT (t), any))
        return false;
+      if (!RECUR (FOR_COND_PREP (t), any))
+       return false;
       tmp = FOR_COND (t);
       if (!RECUR (tmp, rval))
        return false;
@@ -10110,6 +10144,8 @@ potential_constant_expression_1 (tree t, bool 
want_rval, bool strict, bool now,
        return false;
       if (!RECUR (FOR_BODY (t), any))
        return false;
+      if (!RECUR (FOR_COND_CLEANUP (t), any))
+       return false;
       if (breaks (jump_target) || continues (jump_target))
        *jump_target = NULL_TREE;
       return true;
@@ -10126,6 +10162,8 @@ potential_constant_expression_1 (tree t, bool 
want_rval, bool strict, bool now,
       return true;
 
     case WHILE_STMT:
+      if (!RECUR (WHILE_COND_PREP (t), any))
+       return false;
       tmp = WHILE_COND (t);
       if (!RECUR (tmp, rval))
        return false;
@@ -10147,6 +10185,8 @@ potential_constant_expression_1 (tree t, bool 
want_rval, bool strict, bool now,
        }
       if (!RECUR (WHILE_BODY (t), any))
        return false;
+      if (!RECUR (WHILE_COND_CLEANUP (t), any))
+       return false;
       if (breaks (jump_target) || continues (jump_target))
        *jump_target = NULL_TREE;
       return true;
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 3e75b5887506..c5702a9926c0 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -601,6 +601,25 @@ add_decl_expr (tree decl)
   add_stmt (r);
 }
 
+/* Set EXPR_LOCATION on one cleanup T to LOC.  */
+
+static void
+set_one_cleanup_loc (tree t, location_t loc)
+{
+  if (!t)
+    return;
+  if (TREE_CODE (t) != POSTCONDITION_STMT)
+    protected_set_expr_location (t, loc);
+  /* Avoid locus differences for C++ cdtor calls depending on whether
+     cdtor_returns_this: a conversion to void is added to discard the return
+     value, and this conversion ends up carrying the location, and when it
+     gets discarded, the location is lost.  So hold it in the call as well.  */
+  if (TREE_CODE (t) == NOP_EXPR
+      && TREE_TYPE (t) == void_type_node
+      && TREE_CODE (TREE_OPERAND (t, 0)) == CALL_EXPR)
+    protected_set_expr_location (TREE_OPERAND (t, 0), loc);
+}
+
 /* Set EXPR_LOCATION of the cleanups of any CLEANUP_STMT in STMTS to LOC.  */
 
 static void
@@ -608,18 +627,7 @@ set_cleanup_locs (tree stmts, location_t loc)
 {
   if (TREE_CODE (stmts) == CLEANUP_STMT)
     {
-      tree t = CLEANUP_EXPR (stmts);
-      if (t && TREE_CODE (t) != POSTCONDITION_STMT)
-       protected_set_expr_location (t, loc);
-      /* Avoid locus differences for C++ cdtor calls depending on whether
-        cdtor_returns_this: a conversion to void is added to discard the return
-        value, and this conversion ends up carrying the location, and when it
-        gets discarded, the location is lost.  So hold it in the call as
-        well.  */
-      if (TREE_CODE (t) == NOP_EXPR
-         && TREE_TYPE (t) == void_type_node
-         && TREE_CODE (TREE_OPERAND (t, 0)) == CALL_EXPR)
-       protected_set_expr_location (TREE_OPERAND (t, 0), loc);
+      set_one_cleanup_loc (CLEANUP_EXPR (stmts), loc);
       set_cleanup_locs (CLEANUP_BODY (stmts), loc);
     }
   else if (TREE_CODE (stmts) == STATEMENT_LIST)
@@ -777,37 +785,48 @@ finish_cond (tree *cond_p, tree expr)
   *cond_p = expr;
 }
 
-/* If *COND_P specifies a conditional with a declaration, transform the
-   loop such that
+/* If loop condition specifies a conditional with a declaration,
+   such as
            while (A x = 42) { }
            for (; A x = 42;) { }
-   becomes
-           while (true) { A x = 42; if (!x) break; }
-           for (;;) { A x = 42; if (!x) break; }
-   The statement list for BODY will be empty if the conditional did
+   move the *BODY_P statements as a BIND_EXPR into {FOR,WHILE}_COND_PREP
+   and if there is any CLEANUP_STMT at the end, remove that and
+   put the cleanup into {FOR,WHILE}_COND_CLEANUP.
+   genericize_c_loop will then handle it appropriately.  In particular,
+   the {FOR,WHILE}_COND, {FOR,WHILE}_BODY, if used continue label and
+   FOR_EXPR will be appended into the {FOR,WHILE}_COND_PREP BIND_EXPR,
+   but it can't be done too early because only the actual body should
+   bind BREAK_STMT and CONTINUE_STMT to the inner loop.
+   The statement list for *BODY will be empty if the conditional did
    not declare anything.  */
 
 static void
-simplify_loop_decl_cond (tree *cond_p, tree body)
+adjust_loop_decl_cond (tree *body_p, tree *prep_p, tree *cleanup_p)
 {
-  tree cond, if_stmt;
-
-  if (!TREE_SIDE_EFFECTS (body))
+  if (!TREE_SIDE_EFFECTS (*body_p))
     return;
 
-  cond = *cond_p;
-  *cond_p = boolean_true_node;
-
-  if_stmt = begin_if_stmt ();
-  cond_p = &cond;
-  while (TREE_CODE (*cond_p) == ANNOTATE_EXPR)
-    cond_p = &TREE_OPERAND (*cond_p, 0);
-  *cond_p = cp_build_unary_op (TRUTH_NOT_EXPR, *cond_p, false,
-                              tf_warning_or_error);
-  finish_if_stmt_cond (cond, if_stmt);
-  finish_break_stmt ();
-  finish_then_clause (if_stmt);
-  finish_if_stmt (if_stmt);
+  gcc_assert (!processing_template_decl);
+  if (*body_p != cur_stmt_list)
+    {
+      /* There can be either no cleanup at all, if the condition
+        declaration doesn't have non-trivial destructor, or a single
+        one if it does.  In that case extract it into *CLEANUP_P.  */
+      gcc_assert (stmt_list_stack->length () > 1
+                 && (*stmt_list_stack)[stmt_list_stack->length ()
+                                       - 2] == *body_p);
+      tree_stmt_iterator last = tsi_last (*body_p);
+      gcc_assert (tsi_one_before_end_p (last)
+                 && TREE_CODE (tsi_stmt (last)) == CLEANUP_STMT
+                 && CLEANUP_BODY (tsi_stmt (last)) == cur_stmt_list
+                 && tsi_end_p (tsi_last (cur_stmt_list))
+                 && !CLEANUP_EH_ONLY (tsi_stmt (last)));
+      *cleanup_p = CLEANUP_EXPR (tsi_stmt (last));
+      tsi_delink (&last);
+    }
+  current_binding_level->keep = true;
+  *prep_p = *body_p;
+  *body_p = push_stmt_list ();
 }
 
 /* Finish a goto-statement.  */
@@ -1272,7 +1291,8 @@ tree
 begin_while_stmt (void)
 {
   tree r;
-  r = build_stmt (input_location, WHILE_STMT, NULL_TREE, NULL_TREE);
+  r = build_stmt (input_location, WHILE_STMT, NULL_TREE, NULL_TREE,
+                 NULL_TREE, NULL_TREE);
   add_stmt (r);
   WHILE_BODY (r) = do_pushlevel (sk_block);
   begin_cond (&WHILE_COND (r));
@@ -1310,7 +1330,9 @@ finish_while_stmt_cond (tree cond, tree while_stmt, bool 
ivdep,
                                      build_int_cst (integer_type_node,
                                                     annot_expr_no_vector_kind),
                                      integer_zero_node);
-  simplify_loop_decl_cond (&WHILE_COND (while_stmt), WHILE_BODY (while_stmt));
+  adjust_loop_decl_cond (&WHILE_BODY (while_stmt),
+                        &WHILE_COND_PREP (while_stmt),
+                        &WHILE_COND_CLEANUP (while_stmt));
 }
 
 /* Finish a while-statement, which may be given by WHILE_STMT.  */
@@ -1319,8 +1341,14 @@ void
 finish_while_stmt (tree while_stmt)
 {
   end_maybe_infinite_loop (boolean_true_node);
-  WHILE_BODY (while_stmt) = do_poplevel (WHILE_BODY (while_stmt));
+  WHILE_BODY (while_stmt)
+    = (WHILE_COND_PREP (while_stmt)
+       ? pop_stmt_list (WHILE_BODY (while_stmt))
+       : do_poplevel (WHILE_BODY (while_stmt)));
   finish_loop_cond (&WHILE_COND (while_stmt), WHILE_BODY (while_stmt));
+  if (WHILE_COND_PREP (while_stmt))
+    WHILE_COND_PREP (while_stmt) = do_poplevel (WHILE_COND_PREP (while_stmt));
+  set_one_cleanup_loc (WHILE_COND_CLEANUP (while_stmt), input_location);
 }
 
 /* Begin a do-statement.  Returns a newly created DO_STMT if
@@ -1450,7 +1478,7 @@ begin_for_stmt (tree scope, tree init)
   tree r;
 
   r = build_stmt (input_location, FOR_STMT, NULL_TREE, NULL_TREE,
-                 NULL_TREE, NULL_TREE, NULL_TREE);
+                 NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE);
 
   if (scope == NULL_TREE)
     {
@@ -1508,7 +1536,8 @@ finish_for_cond (tree cond, tree for_stmt, bool ivdep, 
tree unroll,
                                  build_int_cst (integer_type_node,
                                                 annot_expr_no_vector_kind),
                                  integer_zero_node);
-  simplify_loop_decl_cond (&FOR_COND (for_stmt), FOR_BODY (for_stmt));
+  adjust_loop_decl_cond (&FOR_BODY (for_stmt), &FOR_COND_PREP (for_stmt),
+                        &FOR_COND_CLEANUP (for_stmt));
 }
 
 /* Finish the increment-EXPRESSION in a for-statement, which may be
@@ -1555,11 +1584,17 @@ finish_for_stmt (tree for_stmt)
     RANGE_FOR_BODY (for_stmt) = do_poplevel (RANGE_FOR_BODY (for_stmt));
   else
     {
-      FOR_BODY (for_stmt) = do_poplevel (FOR_BODY (for_stmt));
+      FOR_BODY (for_stmt)
+       = (FOR_COND_PREP (for_stmt)
+          ? pop_stmt_list (FOR_BODY (for_stmt))
+          : do_poplevel (FOR_BODY (for_stmt)));
       if (FOR_COND (for_stmt))
        finish_loop_cond (&FOR_COND (for_stmt),
                          FOR_EXPR (for_stmt) ? integer_one_node
                                              : FOR_BODY (for_stmt));
+      if (FOR_COND_PREP (for_stmt))
+       FOR_COND_PREP (for_stmt) = do_poplevel (FOR_COND_PREP (for_stmt));
+      set_one_cleanup_loc (FOR_COND_CLEANUP (for_stmt), input_location);
     }
 
   /* Pop the scope for the body of the loop.  */
diff --git a/gcc/testsuite/g++.dg/cpp26/name-independent-decl7.C 
b/gcc/testsuite/g++.dg/cpp26/name-independent-decl7.C
new file mode 100644
index 000000000000..eeb70f83d549
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/name-independent-decl7.C
@@ -0,0 +1,27 @@
+// P2169R4 - A nice placeholder with no name
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wunused-variable -Wunused-but-set-variable 
-Wunused-parameter -Wshadow" }
+
+int bar ();
+
+void
+baz ()
+{
+  for (; int _ = bar (); ++_)
+    int _ = 1;                 // { dg-warning "name-independent declarations 
only available with" "" { target c++23_down } }
+  for (int _ = bar ();
+       int _ = bar (); )       // { dg-warning "name-independent declarations 
only available with" "" { target c++23_down } }
+    int _ = 2;                 // { dg-warning "name-independent declarations 
only available with" "" { target c++23_down } }
+  for (int _ = bar ();
+       int _ = bar (); )       // { dg-warning "name-independent declarations 
only available with" "" { target c++23_down } }
+    ;
+  for (; int _ = bar (); ++_)
+    {
+      int _ = 3;               // { dg-warning "name-independent declarations 
only available with" "" { target c++23_down } }
+    }
+  for (int _ = bar ();
+       int _ = bar (); )       // { dg-warning "name-independent declarations 
only available with" "" { target c++23_down } }
+    {
+      int _ = 4;               // { dg-warning "name-independent declarations 
only available with" "" { target c++23_down } }
+    }
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/name-independent-decl8.C 
b/gcc/testsuite/g++.dg/cpp26/name-independent-decl8.C
new file mode 100644
index 000000000000..62c65b21accc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/name-independent-decl8.C
@@ -0,0 +1,24 @@
+// P2169R4 - A nice placeholder with no name
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+int bar ();
+
+void
+baz ()
+{
+  for (int _ = bar ();
+       int _ = bar (); ++_)    // { dg-warning "name-independent declarations 
only available with" "" { target c++23_down } }
+    ;                          // { dg-error "reference to '_' is ambiguous" 
"" { target *-*-* } .-1 }
+  for (; int _ = bar (); ++_)
+    {
+      int _ = 3;               // { dg-warning "name-independent declarations 
only available with" "" { target c++23_down } }
+      ++_;                     // { dg-error "reference to '_' is ambiguous" }
+    }
+  for (int _ = bar ();
+       int _ = bar (); )       // { dg-warning "name-independent declarations 
only available with" "" { target c++23_down } }
+    {
+      int _ = 4;               // { dg-warning "name-independent declarations 
only available with" "" { target c++23_down } }
+      ++_;                     // { dg-error "reference to '_' is ambiguous" }
+    }
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-86769.C 
b/gcc/testsuite/g++.dg/cpp2a/constexpr-86769.C
new file mode 100644
index 000000000000..d6bf5d78ab53
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-86769.C
@@ -0,0 +1,36 @@
+// PR c++/86769
+// { dg-do compile { target c++20 } }
+
+struct A {
+  int *a;
+  constexpr A (int x) : a (new int (x)) {}
+  constexpr A (const A &x) : a (new int (x.a[0])) {}
+  constexpr ~A () { delete a; }
+  constexpr operator bool () { return *a != 0; }
+};
+
+constexpr int
+foo ()
+{
+  int i = 0;
+  for (A a = 0; A b = a.a[0] < 16; a.a[0] += b.a[0])
+    i += a.a[0] + b.a[0];
+  return i;
+}
+
+static_assert (foo () == 136);
+
+constexpr int
+bar ()
+{
+  int i = 0;
+  A a = 0;
+  while (A b = a.a[0] < 15)
+    {
+      i += a.a[0] + b.a[0];
+      a.a[0] += b.a[0];
+    }
+  return i;
+}
+
+static_assert (bar () == 120);
diff --git a/gcc/testsuite/g++.dg/diagnostic/redeclaration-7.C 
b/gcc/testsuite/g++.dg/diagnostic/redeclaration-7.C
new file mode 100644
index 000000000000..4a5c1bb42e79
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/redeclaration-7.C
@@ -0,0 +1,23 @@
+// PR c++/52953
+// { dg-do compile }
+// { dg-options "-pedantic-errors" }
+
+volatile int v;
+
+void
+baz ()
+{
+  for (int x = v;                      // { dg-message "'int x' previously 
declared here" }
+       int x = v; ++x)                 // { dg-error "redeclaration of 'int 
x'" }
+    ;
+  for (int x = v;                      // { dg-message "'int x' previously 
declared here" }
+       int x = v; ++x)                 // { dg-error "redeclaration of 'int 
x'" }
+    {                                  // { dg-message "'int x' previously 
declared here" "" { target *-*-* } .-1 }
+      int x;                           // { dg-error "redeclaration of 'int 
x'" }
+    }
+  for (int x = v;                      // { dg-message "'int x' previously 
declared here" }
+       int x = v; ++x)                 // { dg-error "redeclaration of 'int 
x'" }
+    {                                  // { dg-message "previous declaration 
'int x'" "" { target *-*-* } .-1 }
+      extern int x;                    // { dg-error "'int x' conflicts with a 
previous declaration" }
+    }
+}
diff --git a/gcc/testsuite/g++.dg/expr/for3.C b/gcc/testsuite/g++.dg/expr/for3.C
new file mode 100644
index 000000000000..e649611d5bd0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/for3.C
@@ -0,0 +1,112 @@
+// PR c++/86769
+// { dg-do run { target c++11 } }
+
+int d, e, f, g, h;
+struct A {
+  int a;
+  A (int x) : a(x) { ++d; ++h; }
+  ~A () { --d; ++h; }
+  A (const A &x) : a(x.a) { ++d; ++h; }
+  operator bool () { return a != 0; }
+};
+struct B {
+  B () { ++e; ++h; }
+  ~B () { --e; ++h; }
+  B (const B &) { ++e; ++h; }
+};
+
+int
+foo (B, int x)
+{
+  if (e != 1)
+    __builtin_abort ();
+  if (f ? x - 1 != (f - 1) % 3 : x)
+    __builtin_abort ();
+  ++f;
+  if (x == 1)
+    return ++g < 3;
+  return 0;
+}
+
+int
+bar (int n)
+{
+  if (e != 0 || d != n)
+    __builtin_abort ();
+  return 0;
+}
+
+int
+main ()
+{
+  for (A a = (bar (0), foo (B {}, 0));
+       A b = (bar (1), foo (B {}, 1));
+       bar (2), foo (B {}, 3))
+    A c = (bar (2), foo (B {}, 2));
+  if (f != 8 || h != 28 || d || e)
+    __builtin_abort ();
+  f = 0; g = -2; h = 0;
+  for (A a = (bar (0), foo (B {}, 0));
+       A b = (bar (1), foo (B {}, 1));
+       bar (2), foo (B {}, 3))
+    {
+      A c = (bar (2), foo (B {}, 2));
+      bar (3);
+    }
+  if (f != 14 || h != 48 || d || e)
+    __builtin_abort ();
+  f = 0; g = 0; h = 0;
+  {
+    A a = (bar (0), foo (B {}, 0));
+    while (A b = (bar (1), foo (B {}, 1)))
+      {
+       A c = (bar (2), foo (B {}, 2));
+       bar (3);
+       foo (B {}, 3);
+      }
+  }
+  if (f != 8 || h != 28 || d || e)
+    __builtin_abort ();
+  f = 0; g = -5; h = 0;
+  for (A a = (bar (0), foo (B {}, 0));
+       A b = (bar (1), foo (B {}, 1));
+       bar (2), foo (B {}, 3))
+    {
+      if (f == 5)
+       {
+         bar (2);
+         foo (B {}, 2);
+         bar (2);
+         continue;
+       }
+      if (f == 11)
+       break;
+      A c = (bar (2), foo (B {}, 2));
+      bar (3);
+    }
+  if (f != 11 || h != 36 || d || e)
+    __builtin_abort ();
+  f = 0; g = -5; h = 0;
+  {
+    A a = (bar (0), foo (B {}, 0));
+    while (A b = (bar (1), foo (B {}, 1)))
+      {
+       if (f == 5)
+         {
+           bar (2);
+           foo (B {}, 2);
+           bar (2);
+           foo (B {}, 3);
+           bar (2);
+           continue;
+         }
+       else if (f == 11)
+         break;
+       A c = (bar (2), foo (B {}, 2));
+       bar (3);
+       foo (B {}, 3);
+      }
+  }
+  if (f != 11 || h != 36 || d || e)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/expr/for4.C b/gcc/testsuite/g++.dg/expr/for4.C
new file mode 100644
index 000000000000..52e9e7126684
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/for4.C
@@ -0,0 +1,116 @@
+// PR c++/86769
+// { dg-do run }
+
+int d, e, f, g, h;
+struct A {
+  int a;
+  A (int x) : a(x) { ++d; ++h; }
+  ~A () { --d; ++h; }
+  A (const A &x) : a(x.a) { ++d; ++h; }
+  operator bool () { return a != 0; }
+};
+struct B {
+  B () { ++e; ++h; }
+  ~B () { --e; ++h; }
+  B (const B &) { ++e; ++h; }
+};
+B k;
+
+int
+foo (B, int x)
+{
+  if (e != 1)
+    __builtin_abort ();
+  if (f ? x - 1 != (f - 1) % 3 : x)
+    __builtin_abort ();
+  ++f;
+  if (x == 1)
+    return ++g < 3;
+  return 0;
+}
+
+int
+bar (int n)
+{
+  if (e != 0 || d != n)
+    __builtin_abort ();
+  return 0;
+}
+
+int
+main ()
+{
+  if (e != 1 || h != 1)
+    __builtin_abort ();
+  e = 0; h = 0;
+  for (A a = (bar (0), foo (k, 0));
+       A b = (bar (1), foo (k, 1));
+       bar (2), foo (k, 3))
+    A c = (bar (2), foo (k, 2));
+  if (f != 8 || h != 28 || d || e)
+    __builtin_abort ();
+  f = 0; g = -2; h = 0;
+  for (A a = (bar (0), foo (k, 0));
+       A b = (bar (1), foo (k, 1));
+       bar (2), foo (k, 3))
+    {
+      A c = (bar (2), foo (k, 2));
+      bar (3);
+    }
+  if (f != 14 || h != 48 || d || e)
+    __builtin_abort ();
+  f = 0; g = 0; h = 0;
+  {
+    A a = (bar (0), foo (k, 0));
+    while (A b = (bar (1), foo (k, 1)))
+      {
+       A c = (bar (2), foo (k, 2));
+       bar (3);
+       foo (k, 3);
+      }
+  }
+  if (f != 8 || h != 28 || d || e)
+    __builtin_abort ();
+  f = 0; g = -5; h = 0;
+  for (A a = (bar (0), foo (k, 0));
+       A b = (bar (1), foo (k, 1));
+       bar (2), foo (k, 3))
+    {
+      if (f == 5)
+       {
+         bar (2);
+         foo (k, 2);
+         bar (2);
+         continue;
+       }
+      if (f == 11)
+       break;
+      A c = (bar (2), foo (k, 2));
+      bar (3);
+    }
+  if (f != 11 || h != 36 || d || e)
+    __builtin_abort ();
+  f = 0; g = -5; h = 0;
+  {
+    A a = (bar (0), foo (k, 0));
+    while (A b = (bar (1), foo (k, 1)))
+      {
+       if (f == 5)
+         {
+           bar (2);
+           foo (k, 2);
+           bar (2);
+           foo (k, 3);
+           bar (2);
+           continue;
+         }
+       else if (f == 11)
+         break;
+       A c = (bar (2), foo (k, 2));
+       bar (3);
+       foo (k, 3);
+      }
+  }
+  if (f != 11 || h != 36 || d || e)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/expr/for5.C b/gcc/testsuite/g++.dg/expr/for5.C
new file mode 100644
index 000000000000..a957fef3760b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/for5.C
@@ -0,0 +1,34 @@
+// PR c++/86769
+// { dg-do run }
+
+int g;
+
+struct X {
+  X () { g++; }
+  ~X () { g--; }
+  operator bool () { return g == 0; }
+};
+
+void
+foo ()
+{
+  if (g <= 0)
+    __builtin_abort ();
+}
+
+void
+bar ()
+{
+  if (g)
+    __builtin_abort ();
+}
+
+int
+main ()
+{
+  for (int i = 0; i < 1; ++i, bar ())
+    {
+      X x = X ();
+      foo ();
+    }
+}
diff --git a/gcc/testsuite/g++.dg/expr/for6.C b/gcc/testsuite/g++.dg/expr/for6.C
new file mode 100644
index 000000000000..624e0535941d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/for6.C
@@ -0,0 +1,39 @@
+// PR c++/86769
+// { dg-do run { target c++11 } }
+
+int v;
+
+struct S {
+  int s;
+  S (int x) : s(x)
+  {
+    if ((v != 0 || s != 0) && (v != 3 || s != 1))
+      __builtin_abort ();
+    ++v;
+  }
+  ~S ()
+  {
+    if ((v != 2 || s != 0) && (v != 4 || s != 1))
+      __builtin_abort ();
+    ++v;
+  }
+  operator bool () const { return true; }
+};
+
+void
+foo (const S &s)
+{
+  if (v != 1 || s.s != 0)
+    __builtin_abort ();
+  ++v;
+}
+
+int
+main ()
+{
+  for (int i = 0; S j{i}; foo (j))
+    {
+      if (++i == 2)
+       break;
+    }
+}
diff --git a/gcc/testsuite/g++.dg/expr/for7.C b/gcc/testsuite/g++.dg/expr/for7.C
new file mode 100644
index 000000000000..59870f4c35cf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/for7.C
@@ -0,0 +1,20 @@
+// PR c++/86769
+// { dg-do run }
+
+int v;
+
+struct S
+{
+  S (int x) : s(x) { v++; }
+  ~S () { v--; }
+  int s;
+  operator int () { if (!v) __builtin_abort (); return s; }
+};
+
+int
+main ()
+{
+  int x = 10;
+  for (int l = 1; S d = x - l; l = d + 1)
+    ;
+}
diff --git a/gcc/testsuite/g++.dg/expr/for8.C b/gcc/testsuite/g++.dg/expr/for8.C
new file mode 100644
index 000000000000..bb41f0c72bb9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/for8.C
@@ -0,0 +1,22 @@
+// PR c++/86769
+// { dg-do run }
+// { dg-options "-O1" }
+
+__attribute__((noipa)) void
+foo (int)
+{
+  static int a = 0;
+  if (++a == 3)
+    __builtin_abort ();
+}
+
+int
+main ()
+{
+  volatile int x = 10;
+  for (int l = 1; int d = x - l; l = d + 1)
+    {
+      int &z = d;
+      foo (z);
+    }
+}
diff --git a/gcc/testsuite/g++.dg/ext/stmtexpr27.C 
b/gcc/testsuite/g++.dg/ext/stmtexpr27.C
new file mode 100644
index 000000000000..f51690a67707
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/stmtexpr27.C
@@ -0,0 +1,64 @@
+// PR c++/86769
+// { dg-do run }
+// { dg-options "" }
+
+struct A {
+  int a;
+  A (int x) : a(x) {}
+  ~A () {}
+  A (const A &x) : a(x.a) {}
+  operator bool () { return a != 0; }
+};
+
+int
+main ()
+{
+  int v = 0;
+  for (int i = 0; i < 3; ++i)
+    for (int j = 0; j < ({ if (i == 1 && j == 1) continue; 4; }); ++j)
+      ++v;
+  if (v != 9)
+    __builtin_abort ();
+  v = 0;
+  for (int i = 0; i < 3; ++i)
+    for (int j = 0; j < ({ if (i == 1 && j == 1) break; 4; }); ++j)
+      ++v;
+  if (v != 5)
+    __builtin_abort ();
+  v = 0;
+  for (int i = 0; i < 3; ++i)
+    for (int j = 0; j < 4; ({ if (i == 1 && j == 1) continue; 1; }), ++j)
+      ++v;
+  if (v != 10)
+    __builtin_abort ();
+  v = 0;
+  for (int i = 0; i < 3; ++i)
+    for (int j = 0; j < 4; ({ if (i == 1 && j == 1) break; 1; }), ++j)
+      ++v;
+  if (v != 6)
+    __builtin_abort ();
+  v = 0;
+  for (int i = 0; i < 3; ++i)
+    for (int j = 0; A c = j < ({ if (i == 1 && j == 1) continue; 4; }); ++j)
+      ++v;
+  if (v != 9)
+    __builtin_abort ();
+  v = 0;
+  for (int i = 0; i < 3; ++i)
+    for (int j = 0; A c = j < ({ if (i == 1 && j == 1) break; 4; }); ++j)
+      ++v;
+  if (v != 5)
+    __builtin_abort ();
+  v = 0;
+  for (int i = 0; i < 3; ++i)
+    for (int j = 0; A c = j < 4; ({ if (i == 1 && j == 1) continue; 1; }), ++j)
+      ++v;
+  if (v != 10)
+    __builtin_abort ();
+  v = 0;
+  for (int i = 0; i < 3; ++i)
+    for (int j = 0; A c = j < 4; ({ if (i == 1 && j == 1) break; 1; }), ++j)
+      ++v;
+  if (v != 6)
+    __builtin_abort ();
+}

Reply via email to