2021-01-19 Jakub Jelinek <ja...@redhat.com>
PR c++/98672
* constexpr.c (check_for_return_continue_data): Add break_stmt member.
(check_for_return_continue): Also look for BREAK_STMT. Handle
SWITCH_STMT
by ignoring break_stmt from its body.
(potential_constant_expression_1) <case FOR_STMT>,
<case WHILE_STMT>: If the condition isn't constant true, check if
the loop body can contain a return stmt.
<case SWITCH_STMT>: Adjust check_for_return_continue_data initializer.
<case IF_STMT>: If recursion with tf_none is successful, merge
*jump_target from the branches - returns with highest priority, breaks
or continues lower. If then branch is potentially constant and
doesn't return, check the else branch if it could return, break or
continue.
* g++.dg/cpp1y/constexpr-98672.C: New test.
--- gcc/cp/constexpr.c.jj 2021-01-14 12:49:50.500644142 +0100
+++ gcc/cp/constexpr.c 2021-01-19 22:44:17.845322567 +0100
@@ -7649,15 +7649,16 @@ check_automatic_or_tls (tree ref)
struct check_for_return_continue_data {
hash_set<tree> *pset;
tree continue_stmt;
+ tree break_stmt;
};
/* Helper function for potential_constant_expression_1 SWITCH_STMT handling,
called through cp_walk_tree. Return the first RETURN_EXPR found, or note
- the first CONTINUE_STMT if RETURN_EXPR is not found. */
+ the first CONTINUE_STMT and/or BREAK_STMT if RETURN_EXPR is not found. */
static tree
check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
{
- tree t = *tp, s;
+ tree t = *tp, s, b;
check_for_return_continue_data *d = (check_for_return_continue_data *) data;
switch (TREE_CODE (t))
{
@@ -7669,6 +7670,11 @@ check_for_return_continue (tree *tp, int
d->continue_stmt = t;
break;
+ case BREAK_STMT:
+ if (d->break_stmt == NULL_TREE)
+ d->break_stmt = t;
+ break;
+
#define RECUR(x) \
if (tree r = cp_walk_tree (&x, check_for_return_continue, data, \
d->pset)) \
@@ -7680,16 +7686,20 @@ check_for_return_continue (tree *tp, int
*walk_subtrees = 0;
RECUR (DO_COND (t));
s = d->continue_stmt;
+ b = d->break_stmt;
RECUR (DO_BODY (t));
d->continue_stmt = s;
+ d->break_stmt = b;
break;
case WHILE_STMT:
*walk_subtrees = 0;
RECUR (WHILE_COND (t));
s = d->continue_stmt;
+ b = d->break_stmt;
RECUR (WHILE_BODY (t));
d->continue_stmt = s;
+ d->break_stmt = b;
break;
case FOR_STMT:
@@ -7698,16 +7708,28 @@ check_for_return_continue (tree *tp, int
RECUR (FOR_COND (t));
RECUR (FOR_EXPR (t));
s = d->continue_stmt;
+ b = d->break_stmt;
RECUR (FOR_BODY (t));
d->continue_stmt = s;
+ d->break_stmt = b;
break;
case RANGE_FOR_STMT:
*walk_subtrees = 0;
RECUR (RANGE_FOR_EXPR (t));
s = d->continue_stmt;
+ b = d->break_stmt;
RECUR (RANGE_FOR_BODY (t));
d->continue_stmt = s;
+ d->break_stmt = b;
+ break;
+
+ case SWITCH_STMT:
+ *walk_subtrees = 0;
+ RECUR (SWITCH_STMT_COND (t));
+ b = d->break_stmt;
+ RECUR (SWITCH_STMT_BODY (t));
+ d->break_stmt = b;
break;
#undef RECUR
@@ -8190,7 +8212,18 @@ potential_constant_expression_1 (tree t,
/* If we couldn't evaluate the condition, it might not ever be
true. */
if (!integer_onep (tmp))
- return true;
+ {
+ /* Before returning true, check if the for body can contain
+ a return. */
+ hash_set<tree> pset;
+ check_for_return_continue_data data = { &pset, NULL_TREE,
+ NULL_TREE };
+ if (tree ret_expr
+ = cp_walk_tree (&FOR_BODY (t), check_for_return_continue,
+ &data, &pset))
+ *jump_target = ret_expr;
+ return true;
+ }
}
if (!RECUR (FOR_EXPR (t), any))
return false;
@@ -8219,7 +8252,18 @@ potential_constant_expression_1 (tree t,
tmp = cxx_eval_outermost_constant_expr (tmp, true);
/* If we couldn't evaluate the condition, it might not ever be true. */
if (!integer_onep (tmp))
- return true;
+ {
+ /* Before returning true, check if the while body can contain
+ a return. */
+ hash_set<tree> pset;
+ check_for_return_continue_data data = { &pset, NULL_TREE,
+ NULL_TREE };
+ if (tree ret_expr
+ = cp_walk_tree (&WHILE_BODY (t), check_for_return_continue,
+ &data, &pset))
+ *jump_target = ret_expr;
+ return true;
+ }
if (!RECUR (WHILE_BODY (t), any))
return false;
if (breaks (jump_target) || continues (jump_target))
@@ -8238,7 +8282,8 @@ potential_constant_expression_1 (tree t,
else
{
hash_set<tree> pset;
- check_for_return_continue_data data = { &pset, NULL_TREE };
+ check_for_return_continue_data data = { &pset, NULL_TREE,
+ NULL_TREE };
if (tree ret_expr
= cp_walk_tree (&SWITCH_STMT_BODY (t), check_for_return_continue,
&data, &pset))
@@ -8648,11 +8693,46 @@ potential_constant_expression_1 (tree t,
return RECUR (TREE_OPERAND (t, 2), want_rval);
else if (TREE_CODE (tmp) == INTEGER_CST)
return RECUR (TREE_OPERAND (t, 1), want_rval);
+ tmp = *jump_target;
for (i = 1; i < 3; ++i)
- if (potential_constant_expression_1 (TREE_OPERAND (t, i),
- want_rval, strict, now,
- tf_none, jump_target))
- return true;
+ {
+ tree this_jump_target = tmp;
+ if (potential_constant_expression_1 (TREE_OPERAND (t, i),
+ want_rval, strict, now,
+ tf_none, &this_jump_target))
+ {
+ if (returns (&this_jump_target))
+ *jump_target = this_jump_target;
+ else if (!returns (jump_target))
+ {
+ if (breaks (&this_jump_target)
+ || continues (&this_jump_target))
+ *jump_target = this_jump_target;
+ if (i == 1)
+ {
+ /* If the then branch is potentially constant, but
+ does not return, check if the else branch
+ couldn't return, break or continue. */
+ hash_set<tree> pset;
+ check_for_return_continue_data data = { &pset, NULL_TREE,
+ NULL_TREE };
+ if (tree ret_expr
+ = cp_walk_tree (&TREE_OPERAND (t, 2),
+ check_for_return_continue, &data,
+ &pset))
+ *jump_target = ret_expr;
+ else if (*jump_target == NULL_TREE)
+ {
+ if (data.continue_stmt)
+ *jump_target = data.continue_stmt;
+ else if (data.break_stmt)
+ *jump_target = data.break_stmt;
+ }
+ }
+ }
+ return true;
+ }
+ }
if (flags & tf_error)
error_at (loc, "expression %qE is not a constant expression", t);
return false;
--- gcc/testsuite/g++.dg/cpp1y/constexpr-98672.C.jj 2021-01-19
22:13:17.031454887 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-98672.C 2021-01-19
22:46:48.989605151 +0100
@@ -0,0 +1,92 @@
+// PR c++/98672
+// { dg-do compile { target c++14 } }
+
+void
+foo ()
+{
+}
+
+constexpr int
+bar ()
+{
+ for (int i = 0; i < 5; ++i)
+ return i;
+ foo ();
+ return 0;
+}
+
+constexpr int
+baz ()
+{
+ int i = 0;
+ while (i < 5)
+ {
+ if (i == 3)
+ return i;
+ else
+ ++i;
+ }
+ foo ();
+ return 0;
+}
+
+constexpr int
+qux (int x)
+{
+ if (x > 10)
+ ++x;
+ else
+ return 7;
+ foo ();
+ return 0;
+}
+
+constexpr int
+corge (int x)
+{
+ for (int a = 1; ; a++)
+ {
+ if (x > 10)
+ ++x;
+ else
+ return 4;
+ foo ();
+ }
+}
+
+constexpr int
+garply (int x)
+{
+ for (int a = 1; ; a++)
+ {
+ if (x > 10)
+ ++x;
+ else
+ break;
+ foo ();
+ }
+ return x;
+}
+
+constexpr int
+waldo (int x)
+{
+ for (int a = 1; ; a++)
+ {
+ if (x > 10)
+ break;
+ else
+ return 5;
+ foo ();
+ }
+ foo ();
+ return x;
+}
+
+constexpr int i = bar ();
+constexpr int j = baz ();
+constexpr int k = qux (4);
+constexpr int l = corge (5);
+constexpr int m = garply (2);
+constexpr int n = waldo (-2);
+static_assert (i == 0 && j == 3 && k == 7 && l == 4 && m == 2 && n == 5, "");
Jakub