On 1/19/21 4:54 PM, Jakub Jelinek wrote:
On Tue, Jan 19, 2021 at 04:01:49PM -0500, Jason Merrill wrote:
Hmm, IF_STMT probably also needs to check the else clause, if the condition
isn't a known constant.

You're right, I thought it was ok because it recurses with tf_none, but
if the then branch is potentially constant and only else returns, continues
or breaks, then as the enhanced testcase shows we were mishandling it too.

So like this then if it passes bootstrap/regtest?

OK.

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


Reply via email to