Hi!

As the testcase shows, the SWITCH_STMT handling in 
potential_constant_expression_1
is quite conservative, it doesn't recurse on the body of the switch stmt,
because at least for the case where the switch condition isn't some easily
determinable constant, we'd need to try all possible values of the switch
expression (or start walking from all possible case labels, see PR for
details), but right now potential_constant_expression_1 doesn't have the
careful stmt skipping logic cxx_eval_* has anyway.

So, the following patch still doesn't recurse on SWITCH_STMT_BODY using
potential_constant_expression_1, but instead checks if the body contains
a return or continue stmt (the latter only if not nested in some loop body
inside of the switch body).  If there is no return nor continue, assuming
there is no endless loop (which wouldn't be constant expression due to the
ops limit) the switch body must fall through to the code after it.
If there is a return or continue, we set *jump_target to it.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2019-03-26  Jakub Jelinek  <ja...@redhat.com>

        PR c++/89785
        * constexpr.c (struct check_for_return_continue_data): New type.
        (check_for_return_continue): New function.
        (potential_constant_expression_1) <case SWITCH_STMT>: Walk
        SWITCH_STMT_BODY to find RETURN_EXPRs or CONTINUE_STMTs not nested
        in loop bodies and set *jump_target to that if found.

        * g++.dg/cpp1y/constexpr-89785-1.C: New test.
        * g++.dg/cpp1y/constexpr-89785-2.C: New test.

--- gcc/cp/constexpr.c.jj       2019-03-22 15:39:58.175990033 +0100
+++ gcc/cp/constexpr.c  2019-03-26 20:35:06.745157623 +0100
@@ -5710,6 +5710,86 @@ check_automatic_or_tls (tree ref)
 }
 #endif
 
+/* Data structure for passing data from potential_constant_expression_1
+   to check_for_return_continue via cp_walk_tree.  */
+struct check_for_return_continue_data {
+  hash_set<tree> *pset;
+  tree continue_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.  */
+static tree
+check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
+{
+  tree t = *tp, s;
+  check_for_return_continue_data *d = (check_for_return_continue_data *) data;
+  switch (TREE_CODE (t))
+    {
+    case RETURN_EXPR:
+      return t;
+
+    case CONTINUE_STMT:
+      if (d->continue_stmt == NULL_TREE)
+       d->continue_stmt = t;
+      break;
+
+#define RECUR(x) \
+      if (tree r = cp_walk_tree (&x, check_for_return_continue, data,  \
+                                d->pset))                              \
+       return r
+
+      /* For loops, walk subtrees manually, so that continue stmts found
+        inside of the bodies of the loops are ignored.  */
+    case DO_STMT:
+      *walk_subtrees = 0;
+      RECUR (DO_COND (t));
+      s = d->continue_stmt;
+      RECUR (DO_BODY (t));
+      d->continue_stmt = s;
+      break;
+
+    case WHILE_STMT:
+      *walk_subtrees = 0;
+      RECUR (WHILE_COND (t));
+      s = d->continue_stmt;
+      RECUR (WHILE_BODY (t));
+      d->continue_stmt = s;
+      break;
+
+    case FOR_STMT:
+      *walk_subtrees = 0;
+      RECUR (FOR_INIT_STMT (t));
+      RECUR (FOR_COND (t));
+      RECUR (FOR_EXPR (t));
+      s = d->continue_stmt;
+      RECUR (FOR_BODY (t));
+      d->continue_stmt = s;
+      break;
+
+    case RANGE_FOR_STMT:
+      *walk_subtrees = 0;
+      RECUR (RANGE_FOR_EXPR (t));
+      s = d->continue_stmt;
+      RECUR (RANGE_FOR_BODY (t));
+      d->continue_stmt = s;
+      break;
+#undef RECUR
+
+    case STATEMENT_LIST:
+    case CONSTRUCTOR:
+      break;
+
+    default:
+      if (!EXPR_P (t))
+       *walk_subtrees = 0;
+      break;
+    }
+
+  return NULL_TREE;
+}
+
 /* Return true if T denotes a potentially constant expression.  Issue
    diagnostic as appropriate under control of FLAGS.  If WANT_RVAL is true,
    an lvalue-rvalue conversion is implied.  If NOW is true, we want to
@@ -6175,7 +6255,24 @@ potential_constant_expression_1 (tree t,
       if (!RECUR (SWITCH_STMT_COND (t), rval))
        return false;
       /* FIXME we don't check SWITCH_STMT_BODY currently, because even
-        unreachable labels would be checked.  */
+        unreachable labels would be checked and it is enough if there is
+        a single switch cond value for which it is a valid constant
+        expression.  We need to check if there are any RETURN_EXPRs
+        or CONTINUE_STMTs inside of the body though, as in that case
+        we need to set *jump_target.  */
+      else
+       {
+         hash_set<tree> pset;
+         check_for_return_continue_data data = { &pset, NULL_TREE };
+         if (tree ret_expr
+             = cp_walk_tree (&SWITCH_STMT_BODY (t), check_for_return_continue,
+                             &data, &pset))
+           /* The switch might return.  */
+           *jump_target = ret_expr;
+         else if (data.continue_stmt)
+           /* The switch can't return, but might continue.  */
+           *jump_target = data.continue_stmt;
+       }
       return true;
 
     case STMT_EXPR:
--- gcc/testsuite/g++.dg/cpp1y/constexpr-89785-1.C.jj   2019-03-26 
20:41:21.199075051 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-89785-1.C      2019-03-26 
20:33:05.498127144 +0100
@@ -0,0 +1,36 @@
+// PR c++/89785
+// { dg-do compile { target c++14 } }
+
+constexpr int
+foo (int x)
+{
+  switch (x)
+    {
+    case 0:
+      throw -42;
+    case 2:
+      return 42;
+    }
+  throw 42;
+}
+
+constexpr int
+bar (int x)
+{
+  do
+    {
+      switch (x)
+       {
+       case 0:
+         throw 42;
+       case 1:
+         continue;
+       }
+      throw -42;
+    }
+  while (0);
+  return x;
+}
+
+static_assert (foo (2) == 42, "");
+static_assert (bar (1) == 1, "");
--- gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C.jj   2019-03-26 
20:41:37.625808219 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C      2019-03-26 
20:44:12.833287044 +0100
@@ -0,0 +1,36 @@
+// PR c++/89785
+// { dg-do compile { target c++14 } }
+
+constexpr int
+foo (int x)
+{
+  switch (x)
+    {
+    case 0:
+      break;
+    case 2:
+      break;
+    }
+  throw 42;    // { dg-error "is not a constant expression" }
+  return 0;
+}
+
+constexpr int
+bar (int x)
+{
+  do
+    {
+      switch (x)
+       {
+       case 0:
+         throw 42;
+       case 1:
+         for (int i = 0; i < 10; i++)
+           continue;
+         break;
+       }
+      throw -42;       // { dg-error "is not a constant expression" }
+    }
+  while (0);
+  return x;
+}

        Jakub

Reply via email to