/* WHILE_STMT accessors. These give access to the condition of the
while statement, the body, and name of the while statement, and
- condition preparation statements and its cleanup, respectively. */
+ condition preparation statements and number of its nested cleanups,
+ 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)
/* FOR_STMT accessors. These give access to the init statement,
condition, update expression, body and name of the for statement,
- and condition preparation statements and its cleanup, respectively. */
+ and condition preparation statements and number of its nested cleanups,
+ 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)
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. */
+ non-NULL should be appended, and COND_CLEANUP is number of nested
+ CLEANUP_STMT -> TRY_FINALLY_EXPR statements at the end. If non-NULL,
+ COND, BODY, continue label if needed and INCR if non-NULL should be
+ appended to the body of the COND_CLEANUP's nested TRY_FINALLY_EXPR. */
static void
genericize_c_loop (tree *stmt_p, location_t start_locus, tree cond, tree body,
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);
EXPR;
goto top;
- or
-
- try {
- if (COND); else break;
- BODY;
- cont:
- EXPR;
- } finally {
- COND_CLEANUP
- }
-
- appended into COND_PREP body. */
+ appended into COND_PREP body or body of some TRY_FINALLY_EXPRs
+ at the end of COND_PREP. */
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;
- }
+ if (cond_cleanup)
+ for (unsigned depth = tree_to_uhwi (cond_cleanup); depth; --depth)
+ {
+ t = tsi_stmt (tsi_last (*stmt_list_p));
+ gcc_assert (TREE_CODE (t) == TRY_FINALLY_EXPR);
+ stmt_list_p = &TREE_OPERAND (t, 0);
+ }
+ stmt_list = *stmt_list_p;
+ *stmt_list_p = 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));
tree body, cond = NULL_TREE, expr = NULL_TREE;
tree cond_prep = NULL_TREE, cond_cleanup = NULL_TREE;
+ unsigned cond_cleanup_depth = 0;
int count = 0;
switch (TREE_CODE (t))
{
}
if (cond_prep)
gcc_assert (TREE_CODE (cond_prep) == BIND_EXPR);
- auto cleanup_cond = [=] {
+ 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_cleanup_depth && !*non_constant_p)
+ {
+ auto_vec<tree, 4> cleanups (cond_cleanup_depth);
+ tree s = BIND_EXPR_BODY (cond_prep);
+ unsigned i;
+ for (i = cond_cleanup_depth; i; --i)
+ {
+ tree_stmt_iterator iter = tsi_last (s);
+ s = tsi_stmt (iter);
+ cleanups.quick_push (CLEANUP_EXPR (s));
+ s = CLEANUP_BODY (s);
+ }
+ tree c;
+ FOR_EACH_VEC_ELT_REVERSE (cleanups, i, c)
+ cxx_eval_constant_expression (ctx, c, vc_discard, non_constant_p,
+ overflow_p);
+ }
if (cond_prep)
for (tree decl = BIND_EXPR_VARS (cond_prep);
decl; decl = DECL_CHAIN (decl))
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_cleanup)
+ {
+ /* If COND_CLEANUP is non-NULL, we need to evaluate DEPTH
+ nested STATEMENT_LISTs from inside of BIND_EXPR_BODY,
+ but defer the evaluation of CLEANUP_EXPRs of CLEANUP_STMT
+ at the end of those STATEMENT_LISTs. */
+ cond_cleanup_depth = 0;
+ tree s = BIND_EXPR_BODY (cond_prep);
+ for (unsigned depth = tree_to_uhwi (cond_cleanup);
+ depth; --depth)
+ {
+ for (tree_stmt_iterator i = tsi_start (s);
+ !tsi_end_p (i); ++i)
+ {
+ tree stmt = *i;
+ if (TREE_CODE (stmt) == DEBUG_BEGIN_STMT)
+ continue;
+ if (tsi_one_before_end_p (i))
+ {
+ /* The last statement in the STATEMENT_LIST
+ has to be a CLEANUP_STMT (verified in
+ finish_loop_cond_prep). We want to
+ evaluate just its CLEANUP_BODY part but not
+ CLEANUP_EXPR part just yet. */
+ gcc_assert (TREE_CODE (stmt) == CLEANUP_STMT);
+ /* If the CLEANUP_STMT is not actually to be
+ evaluated, don't increment cond_cleanup_depth
+ so that we don't evaluate the CLEANUP_EXPR
+ for it later either. */
+ if (*jump_target)
+ {
+ depth = 1;
+ break;
+ }
+ ++cond_cleanup_depth;
+ /* If not in the innermost one, next iteration
+ will handle CLEANUP_BODY similarly. */
+ if (depth > 1)
+ {
+ s = CLEANUP_BODY (stmt);
+ break;
+ }
+ /* The innermost one can be evaluated normally. */
+ cxx_eval_constant_expression (ctx,
+ CLEANUP_BODY (stmt),
+ vc_discard,
+ non_constant_p,
+ overflow_p,
+ jump_target);
+ break;
+ }
+ /* And so should be evaluated statements which aren't
+ last in the STATEMENT_LIST. */
+ cxx_eval_constant_expression (ctx, stmt, vc_discard,
+ non_constant_p, overflow_p,
+ jump_target);
+ if (*non_constant_p
+ || returns (jump_target)
+ || breaks (jump_target)
+ || continues (jump_target))
+ {
+ depth = 1;
+ break;
+ }
+ }
+ }
+ }
+ else
+ cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (cond_prep),
+ vc_discard, non_constant_p,
+ overflow_p, jump_target);
}
if (cond)
while (A x = 42) { }
for (; A x = 42;) { }
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.
+ and if there are any CLEANUP_STMT at the end, remember their count in
+ {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,
return;
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);
+ *prep_p = *body_p;
+ if (*prep_p != cur_stmt_list)
+ {
+ /* There can be just one CLEANUP_STMT, or there could be multiple
+ nested CLEANUP_STMTs, e.g. for structured bindings used as
+ condition. */
+ gcc_assert (stmt_list_stack->length () > 1);
+ for (unsigned i = stmt_list_stack->length () - 2; ; --i)
+ {
+ tree t = (*stmt_list_stack)[i];
+ tree_stmt_iterator last = tsi_last (t);
+ gcc_assert (tsi_one_before_end_p (last)
+ && TREE_CODE (tsi_stmt (last)) == CLEANUP_STMT
+ && (CLEANUP_BODY (tsi_stmt (last))
+ == (*stmt_list_stack)[i + 1])
+ && !CLEANUP_EH_ONLY (tsi_stmt (last)));
+ if (t == *prep_p)
+ {
+ *cleanup_p = build_int_cst (long_unsigned_type_node,
+ stmt_list_stack->length () - 1 - i);
+ break;
+ }
+ gcc_assert (i >= 1);
+ }
}
current_binding_level->keep = true;
- *prep_p = *body_p;
- *body_p = push_stmt_list ();
+ tree_stmt_iterator iter = tsi_last (cur_stmt_list);
+ /* Temporarily store in {FOR,WHILE}_BODY the last statement of
+ the innnermost statement list or NULL if it has no statement.
+ This is used in finish_loop_cond_prep to find out the splitting
+ point and then {FOR,WHILE}_BODY will be changed to the actual
+ body. */
+ if (tsi_end_p (iter))
+ *body_p = NULL_TREE;
+ else
+ *body_p = tsi_stmt (iter);
+}
+
+/* Finalize {FOR,WHILE}_{BODY,COND_PREP} after the loop body.
+ The above function initialized *BODY_P to the last statement
+ in *PREP_P at that point.
+ Call do_poplevel on *PREP_P and move everything after that
+ former last statement into *BODY_P. genericize_c_loop
+ will later put those parts back together.
+ CLEANUP is {FOR,WHILE}_COND_CLEANUP. */
+
+static void
+finish_loop_cond_prep (tree *body_p, tree *prep_p, tree cleanup)
+{
+ *prep_p = do_poplevel (*prep_p);
+ gcc_assert (TREE_CODE (*prep_p) == BIND_EXPR);
+ if (BIND_EXPR_BODY (*prep_p) == *body_p)
+ {
+ gcc_assert (cleanup == NULL_TREE);
+ *body_p = build_empty_stmt (input_location);
+ return;
+ }
+ tree stmt_list = BIND_EXPR_BODY (*prep_p);
+ gcc_assert (TREE_CODE (stmt_list) == STATEMENT_LIST);
+ if (cleanup)
+ {
+ tree_stmt_iterator iter = tsi_last (stmt_list);
+ gcc_assert (TREE_CODE (tsi_stmt (iter)) == CLEANUP_STMT);
+ for (unsigned depth = tree_to_uhwi (cleanup); depth > 1; --depth)
+ {
+ gcc_assert (TREE_CODE (CLEANUP_BODY (tsi_stmt (iter)))
+ == STATEMENT_LIST);
+ iter = tsi_last (CLEANUP_BODY (tsi_stmt (iter)));
+ gcc_assert (TREE_CODE (tsi_stmt (iter)) == CLEANUP_STMT);
+ }
+ if (*body_p == NULL_TREE)
+ {
+ *body_p = CLEANUP_BODY (tsi_stmt (iter));
+ CLEANUP_BODY (tsi_stmt (iter)) = build_empty_stmt (input_location);
+ return;
+ }
+ stmt_list = CLEANUP_BODY (tsi_stmt (iter));
+ }
+ tree_stmt_iterator iter = tsi_start (stmt_list);
+ while (tsi_stmt (iter) != *body_p)
+ tsi_next (&iter);
+ *body_p = tsi_split_stmt_list (input_location, iter);
}
/* Finish a goto-statement. */
finish_while_stmt (tree while_stmt)
{
end_maybe_infinite_loop (boolean_true_node);
- 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);
+ finish_loop_cond_prep (&WHILE_BODY (while_stmt),
+ &WHILE_COND_PREP (while_stmt),
+ WHILE_COND_CLEANUP (while_stmt));
+ else
+ WHILE_BODY (while_stmt) = do_poplevel (WHILE_BODY (while_stmt));
+ finish_loop_cond (&WHILE_COND (while_stmt), WHILE_BODY (while_stmt));
}
/* Begin a do-statement. Returns a newly created DO_STMT if
RANGE_FOR_BODY (for_stmt) = do_poplevel (RANGE_FOR_BODY (for_stmt));
else
{
- 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_PREP (for_stmt))
+ finish_loop_cond_prep (&FOR_BODY (for_stmt),
+ &FOR_COND_PREP (for_stmt),
+ FOR_COND_CLEANUP (for_stmt));
+ else
+ 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. */
--- /dev/null
+// P0963R3 - Structured binding declaration as a condition
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+namespace std {
+ template<typename T> struct tuple_size;
+ template<int, typename> struct tuple_element;
+}
+
+struct S {
+ S () : s (0) {}
+ S (int x) : s (x) {}
+ S (const S &x) : s (x.s) {}
+ ~S () {}
+ int s;
+};
+
+struct T {
+ S a, b, c;
+ ~T () {}
+ explicit operator bool () const noexcept { return a.s == b.s; }
+ template <int I> S get () { return I ? a : b; }
+};
+
+template<> struct std::tuple_size<T> { static const int value = 2; };
+template<int I> struct std::tuple_element<I,T> { using type = S; };
+
+void
+foo (T t, bool x)
+{
+ while (auto [ i, j ] = T { 1, 1, 3 }) // { dg-warning "structured bindings in conditions only available with" "" { target c++23_down } }
+ {
+ if (x)
+ break;
+ }
+}
+
+void
+bar (T t, bool x)
+{
+ for (int cnt = 0; auto [ i, j ] = T { 2, 2, 4 }; ++cnt) // { dg-warning "structured bindings in conditions only available with" "" { target c++23_down } }
+ {
+ if (x)
+ break;
+ }
+}
--- /dev/null
+// PR c++/118822
+// { dg-do compile }
+
+struct A { A (); ~A (); };
+bool baz ();
+
+void
+foo ()
+{
+ while (bool x = baz ())
+ {
+ lab:;
+ A a;
+ }
+}
+
+void
+bar ()
+{
+ for (bool y = baz (); bool x = baz (); y |= x)
+ {
+ lab:;
+ A a;
+ }
+}
i->ptr = next;
}
+/* Split a STATEMENT_LIST in I.contrainer into two, all statements
+ from the start until I.ptr inclusive will remain in the original
+ one, all statements after I.ptr are removed from that STATEMENT_LIST
+ and returned as a new STATEMENT_LIST. If I is the last statement,
+ an empty statement with LOC location is returned. */
+
+tree
+tsi_split_stmt_list (location_t loc, tree_stmt_iterator i)
+{
+ if (tsi_one_before_end_p (i))
+ return build_empty_stmt (loc);
+ tsi_next (&i);
+ tree ret = NULL_TREE;
+ while (!tsi_end_p (i))
+ {
+ tree t = tsi_stmt (i);
+ tsi_delink (&i);
+ append_to_statement_list_force (t, &ret);
+ }
+ return ret;
+}
+
/* Return the first expression in a sequence of COMPOUND_EXPRs, or in
a STATEMENT_LIST, disregarding DEBUG_BEGIN_STMTs, recursing into a
STATEMENT_LIST if that's the first non-DEBUG_BEGIN_STMT. */
enum tsi_iterator_update);
extern void tsi_delink (tree_stmt_iterator *);
+extern tree tsi_split_stmt_list (location_t, tree_stmt_iterator);
extern tree alloc_stmt_list (void);
extern void free_stmt_list (tree);