re PR c++/89785 (Incorrect "not a constant expression" error with switch statement...
authorJakub Jelinek <jakub@redhat.com>
Thu, 28 Mar 2019 14:47:47 +0000 (15:47 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Thu, 28 Mar 2019 14:47:47 +0000 (15:47 +0100)
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.

From-SVN: r269995

gcc/cp/ChangeLog
gcc/cp/constexpr.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp1y/constexpr-89785-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C [new file with mode: 0644]

index 9803ef947f803bc1c59810df0b0487b55cbf9bcc..b7c6c35a16035cdae5faccc1e60a4cf73a4e5f70 100644 (file)
@@ -1,3 +1,12 @@
+2019-03-28  Jakub Jelinek  <jakub@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.
+
 2019-03-27  Jason Merrill  <jason@redhat.com>
 
        PR c++/89831 - error with qualified-id in const member function.
index c00d642fcfeda2167d250ca834cec8fae394a2f1..daf34e1078418b239fa3b49bac991aba01a4e6e5 100644 (file)
@@ -5731,6 +5731,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
@@ -6196,7 +6276,24 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
       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:
index f29455331f51857c8134141bf2d2884e56eb35da..64457695b20c69e7cd107930158819bf7ea702f9 100644 (file)
@@ -1,3 +1,9 @@
+2019-03-28  Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/89785
+       * g++.dg/cpp1y/constexpr-89785-1.C: New test.
+       * g++.dg/cpp1y/constexpr-89785-2.C: New test.
+
 2019-03-27  Janus Weil  <janus@gcc.gnu.org>
 
        PR fortran/85537
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-1.C
new file mode 100644 (file)
index 0000000..10f16b9
--- /dev/null
@@ -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, "");
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-89785-2.C
new file mode 100644 (file)
index 0000000..5cd46c7
--- /dev/null
@@ -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;
+}