On 2/6/25 1:21 PM, Jakub Jelinek wrote:
On Thu, Feb 06, 2025 at 10:40:52AM -0500, Jason Merrill wrote:
Thanks.

@@ -7202,6 +7210,28 @@ cxx_eval_loop_expr (const constexpr_ctx
            cxx_eval_constant_expression (ctx, expr, vc_prvalue,
                                          non_constant_p, overflow_p,
                                          jump_target);
+
+         if (cond_cleanup && !*non_constant_p)
+           {
+             iloc_sentinel ils (loc);

What does this do, given that you're setting the locations on the cleanups?

I guess this was copied from the CLEANUP_STMT handling, but I'm not sure why
it's there either; commenting it out there doesn't seem to break any
constexpr* tests.

Indeed, it also looked weird to me.

Let's share this repeated code with the copy below by using a lambda, as in
the attached.

So like this?

So far tested with
GXX_TESTSUITE_STDS=98,11,14,17,20,23,26 make check-g++ \
RUNTESTFLAGS="debug.exp=cleanup1.C dg.exp='name-independent-decl*.C 
redeclaration-*.C pr18770.C for*.C stmtexpr* constexpr-86769.C' old-deja.exp=cond.C"
Ok for trunk if it passes full bootstrap/regtest?

OK.

2025-02-06  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.

--- gcc/c-family/c-common.def.jj        2025-02-05 13:14:34.464202519 +0100
+++ gcc/c-family/c-common.def   2025-02-06 18:50:18.191080462 +0100
@@ -58,13 +58,14 @@ DEFTREECODE (SIZEOF_EXPR, "sizeof_expr",
  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, FOR_SCOPE, and FOR_NAME
-   respectively.  */
-DEFTREECODE (FOR_STMT, "for_stmt", tcc_statement, 6)
+   FOR_INIT_STMT, FOR_COND, FOR_EXPR, FOR_BODY, FOR_SCOPE, FOR_NAME,
+   FOR_COND_PREP and FOR_COND_CLEANUP, respectively.  */
+DEFTREECODE (FOR_STMT, "for_stmt", tcc_statement, 8)
/* Used to represent a 'while' statement. The operands are WHILE_COND,
-   WHILE_BODY, and WHILE_NAME, respectively.  */
-DEFTREECODE (WHILE_STMT, "while_stmt", tcc_statement, 3)
+   WHILE_BODY, WHILE_NAME, WHILE_COND_PREP and WHILE_COND_CLEANUP,
+   respectively.  */
+DEFTREECODE (WHILE_STMT, "while_stmt", tcc_statement, 5)
/* Used to represent a 'do' statement. The operands are DO_COND, DO_BODY,
     and DO_NAME, respectively.  */
--- gcc/c-family/c-common.h.jj  2025-02-05 13:14:34.505201941 +0100
+++ gcc/c-family/c-common.h     2025-02-06 18:50:18.198080368 +0100
@@ -1517,10 +1517,13 @@ extern tree build_userdef_literal (tree
/* WHILE_STMT accessors. These give access to the condition of the
-   while statement, the body and name of the while statement, respectively.  */
+   while statement, the body, and name 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_NAME(NODE)      TREE_OPERAND (WHILE_STMT_CHECK (NODE), 2)
+#define WHILE_COND_PREP(NODE)  TREE_OPERAND (WHILE_STMT_CHECK (NODE), 3)
+#define WHILE_COND_CLEANUP(NODE) TREE_OPERAND (WHILE_STMT_CHECK (NODE), 4)
/* DO_STMT accessors. These give access to the condition of the do
     statement, the body and name of the do statement, respectively.  */
@@ -1530,13 +1533,15 @@ extern tree build_userdef_literal (tree
/* FOR_STMT accessors. These give access to the init statement,
     condition, update expression, body and name 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_NAME(NODE)                TREE_OPERAND (FOR_STMT_CHECK (NODE), 5)
+#define FOR_COND_PREP(NODE)    TREE_OPERAND (FOR_STMT_CHECK (NODE), 6)
+#define FOR_COND_CLEANUP(NODE) TREE_OPERAND (FOR_STMT_CHECK (NODE), 7)
/* BREAK_STMT accessors. */
  #define BREAK_NAME(NODE)      TREE_OPERAND (BREAK_STMT_CHECK (NODE), 0)
--- gcc/c-family/c-gimplify.cc.jj       2025-02-05 13:14:34.666199667 +0100
+++ gcc/c-family/c-gimplify.cc  2025-02-06 18:54:47.436460396 +0100
@@ -254,24 +254,31 @@ expr_loc_or_loc (const_tree expr, locati
     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, tree name, bool cond_is_first,
-                  int *walk_subtrees, void *data, walk_tree_fn func,
-                  walk_tree_lh lh)
+                  tree incr, tree name, 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);
@@ -284,9 +291,66 @@ genericize_c_loop (tree *stmt_p, locatio
    if (name)
      release_named_bc (name);
- /* 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,
@@ -383,6 +447,11 @@ genericize_c_loop (tree *stmt_p, locatio
        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);
@@ -408,7 +477,8 @@ genericize_for_stmt (tree *stmt_p, int *
      }
genericize_c_loop (&loop, EXPR_LOCATION (stmt), FOR_COND (stmt),
-                    FOR_BODY (stmt), FOR_EXPR (stmt), FOR_NAME (stmt), 1,
+                    FOR_BODY (stmt), FOR_EXPR (stmt), FOR_NAME (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)
@@ -424,7 +494,8 @@ genericize_while_stmt (tree *stmt_p, int
  {
    tree stmt = *stmt_p;
    genericize_c_loop (stmt_p, EXPR_LOCATION (stmt), WHILE_COND (stmt),
-                    WHILE_BODY (stmt), NULL_TREE, WHILE_NAME (stmt), 1,
+                    WHILE_BODY (stmt), NULL_TREE, WHILE_NAME (stmt),
+                    WHILE_COND_PREP (stmt), WHILE_COND_CLEANUP (stmt), 1,
                     walk_subtrees, data, func, lh);
  }
@@ -436,8 +507,8 @@ genericize_do_stmt (tree *stmt_p, int *w
  {
    tree stmt = *stmt_p;
    genericize_c_loop (stmt_p, EXPR_LOCATION (stmt), DO_COND (stmt),
-                    DO_BODY (stmt), NULL_TREE, DO_NAME (stmt), 0,
-                    walk_subtrees, data, func, lh);
+                    DO_BODY (stmt), NULL_TREE, DO_NAME (stmt),
+                    NULL_TREE, NULL_TREE, 0, walk_subtrees, data, func, lh);
  }
/* Genericize a SWITCH_STMT node *STMT_P by turning it into a SWITCH_EXPR. */
--- gcc/c/c-parser.cc.jj        2025-02-05 13:14:34.742198595 +0100
+++ gcc/c/c-parser.cc   2025-02-06 18:50:18.214080153 +0100
@@ -8802,7 +8802,8 @@ c_parser_while_statement (c_parser *pars
    body = c_parser_c99_block_statement (parser, if_p, &loc_after_labels);
    if (loop_name && !C_DECL_LOOP_SWITCH_NAME_USED (loop_name))
      loop_name = NULL_TREE;
-  add_stmt (build_stmt (loc, WHILE_STMT, cond, body, loop_name));
+  add_stmt (build_stmt (loc, WHILE_STMT, cond, body, loop_name, NULL_TREE,
+                       NULL_TREE));
    add_stmt (c_end_compound_stmt (loc, block, flag_isoc99));
    c_parser_maybe_reclassify_token (parser);
    if (num_names)
@@ -9207,7 +9208,7 @@ c_parser_for_statement (c_parser *parser
      add_stmt (build_stmt (for_loc, FOR_STMT, NULL_TREE, cond, incr,
                          body, NULL_TREE,
                          loop_name && C_DECL_LOOP_SWITCH_NAME_USED (loop_name)
-                         ? loop_name : NULL_TREE));
+                         ? loop_name : 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);
--- gcc/cp/semantics.cc.jj      2025-02-05 13:14:34.823197451 +0100
+++ gcc/cp/semantics.cc 2025-02-06 18:59:19.089807954 +0100
@@ -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
  {
    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. */
@@ -1368,7 +1387,8 @@ tree
  begin_while_stmt (void)
  {
    tree r;
-  r = build_stmt (input_location, WHILE_STMT, NULL_TREE, NULL_TREE, NULL_TREE);
+  r = build_stmt (input_location, WHILE_STMT, NULL_TREE, NULL_TREE, NULL_TREE,
+                 NULL_TREE, NULL_TREE);
    add_stmt (r);
    WHILE_BODY (r) = do_pushlevel (sk_block);
    begin_cond (&WHILE_COND (r));
@@ -1406,7 +1426,9 @@ finish_while_stmt_cond (tree cond, tree
                                      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. */
@@ -1415,8 +1437,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
@@ -1547,7 +1575,8 @@ 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,
+                 NULL_TREE, NULL_TREE);
if (scope == NULL_TREE)
      {
@@ -1605,7 +1634,8 @@ finish_for_cond (tree cond, tree for_stm
                                  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
@@ -1679,11 +1709,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. */
--- gcc/cp/constexpr.cc.jj      2025-02-06 15:38:41.679410238 +0100
+++ gcc/cp/constexpr.cc 2025-02-06 19:01:40.642908996 +0100
@@ -7152,6 +7152,7 @@ cxx_eval_loop_expr (const constexpr_ctx
      }
tree body, cond = NULL_TREE, expr = NULL_TREE;
+  tree cond_prep = NULL_TREE, cond_cleanup = NULL_TREE;
    int count = 0;
    switch (TREE_CODE (t))
      {
@@ -7165,6 +7166,8 @@ cxx_eval_loop_expr (const constexpr_ctx
      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:
@@ -7176,11 +7179,25 @@ cxx_eval_loop_expr (const constexpr_ctx
        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)
@@ -7202,6 +7219,17 @@ cxx_eval_loop_expr (const constexpr_ctx
            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)
@@ -7239,6 +7267,8 @@ cxx_eval_loop_expr (const constexpr_ctx
         && (!switches (jump_target) || count == 0)
         && !*non_constant_p);
+ cleanup_cond ();
+
    return NULL_TREE;
  }
@@ -9618,6 +9648,7 @@ check_for_return_continue (tree *tp, int case WHILE_STMT:
        *walk_subtrees = 0;
+      RECUR (WHILE_COND_PREP (t));
        RECUR (WHILE_COND (t));
        s = d->continue_stmt;
        b = d->break_stmt;
@@ -9629,6 +9660,7 @@ check_for_return_continue (tree *tp, int
      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;
@@ -10132,6 +10164,8 @@ potential_constant_expression_1 (tree t,
      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;
@@ -10159,6 +10193,8 @@ potential_constant_expression_1 (tree t,
        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;
@@ -10175,6 +10211,8 @@ potential_constant_expression_1 (tree t,
        return true;
case WHILE_STMT:
+      if (!RECUR (WHILE_COND_PREP (t), any))
+       return false;
        tmp = WHILE_COND (t);
        if (!RECUR (tmp, rval))
        return false;
@@ -10196,6 +10234,8 @@ potential_constant_expression_1 (tree t,
        }
        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;
--- gcc/testsuite/g++.dg/diagnostic/redeclaration-7.C.jj        2025-02-06 
18:50:18.217080113 +0100
+++ gcc/testsuite/g++.dg/diagnostic/redeclaration-7.C   2025-02-06 
18:50:18.217080113 +0100
@@ -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" }
+    }
+}
--- gcc/testsuite/g++.dg/expr/for3.C.jj 2025-02-06 18:50:18.217080113 +0100
+++ gcc/testsuite/g++.dg/expr/for3.C    2025-02-06 18:50:18.217080113 +0100
@@ -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 ();
+}
--- gcc/testsuite/g++.dg/expr/for4.C.jj 2025-02-06 18:50:18.217080113 +0100
+++ gcc/testsuite/g++.dg/expr/for4.C    2025-02-06 18:50:18.217080113 +0100
@@ -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 ();
+}
--- gcc/testsuite/g++.dg/expr/for5.C.jj 2025-02-06 18:50:18.218080099 +0100
+++ gcc/testsuite/g++.dg/expr/for5.C    2025-02-06 18:50:18.218080099 +0100
@@ -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 ();
+    }
+}
--- gcc/testsuite/g++.dg/expr/for6.C.jj 2025-02-06 18:50:18.218080099 +0100
+++ gcc/testsuite/g++.dg/expr/for6.C    2025-02-06 18:50:18.218080099 +0100
@@ -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;
+    }
+}
--- gcc/testsuite/g++.dg/expr/for7.C.jj 2025-02-06 18:50:18.218080099 +0100
+++ gcc/testsuite/g++.dg/expr/for7.C    2025-02-06 18:50:18.218080099 +0100
@@ -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)
+    ;
+}
--- gcc/testsuite/g++.dg/expr/for8.C.jj 2025-02-06 18:50:18.218080099 +0100
+++ gcc/testsuite/g++.dg/expr/for8.C    2025-02-06 18:50:18.218080099 +0100
@@ -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);
+    }
+}
--- gcc/testsuite/g++.dg/ext/stmtexpr27.C.jj    2025-02-06 18:50:18.218080099 
+0100
+++ gcc/testsuite/g++.dg/ext/stmtexpr27.C       2025-02-06 18:50:18.218080099 
+0100
@@ -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 ();
+}
--- gcc/testsuite/g++.dg/cpp2a/constexpr-86769.C.jj     2025-02-06 
18:50:18.218080099 +0100
+++ gcc/testsuite/g++.dg/cpp2a/constexpr-86769.C        2025-02-06 
18:50:18.218080099 +0100
@@ -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);
--- gcc/testsuite/g++.dg/cpp26/name-independent-decl7.C.jj      2025-02-06 
18:50:18.230079938 +0100
+++ gcc/testsuite/g++.dg/cpp26/name-independent-decl7.C 2025-02-06 
18:50:18.230079938 +0100
@@ -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 } }
+    }
+}
--- gcc/testsuite/g++.dg/cpp26/name-independent-decl8.C.jj      2025-02-06 
18:50:18.230079938 +0100
+++ gcc/testsuite/g++.dg/cpp26/name-independent-decl8.C 2025-02-06 
18:50:18.230079938 +0100
@@ -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" }
+    }
+}


        Jakub


Reply via email to