Hi!

cxx_eval_switch_expr assumes that SWITCH_EXPR's body is always a
STATEMENT_LIST, but that doesn't have to be the case.
As the testcase shows, if there are any variable declarations in the
switch body, it can be also a BIND_EXPR, which cxx_eval_constant_expression
handles properly, and as bar in the testcase shows, it can be also just
a single statement (as try isn't allowed in constexpr functions, I think
we just want to do what cxx_eval_statement_list would do on such a statement
if it was wrapped into a STATEMENT_LIST - ignore it, as the case NNN: or 
default:
is not present.

Bootstrapped/regtested on x86_64-linux and i686-linux?  What about older
branches?

2016-09-05  Jakub Jelinek  <ja...@redhat.com>

        PR c++/77467
        * constexpr.c (cxx_eval_switch_expr): Call cxx_eval_constant_expression
        instead of cxx_eval_statement_list, for body other than STATEMENT_LIST
        or BIND_EXPR don't evaluate the body at all.

        * g++.dg/cpp1y/constexpr-77467.C: New test.

--- gcc/cp/constexpr.c.jj       2016-08-30 08:42:06.000000000 +0200
+++ gcc/cp/constexpr.c  2016-09-05 11:34:30.185518395 +0200
@@ -3572,8 +3572,12 @@ cxx_eval_switch_expr (const constexpr_ct
   *jump_target = cond;
 
   tree body = TREE_OPERAND (t, 1);
-  cxx_eval_statement_list (ctx, body,
-                          non_constant_p, overflow_p, jump_target);
+  /* If body is a statement other than STATEMENT_LIST or BIND_EXPR,
+     it should be skipped.  E.g. switch (a) b = a;  */
+  if (TREE_CODE (body) == STATEMENT_LIST
+      || TREE_CODE (body) == BIND_EXPR)
+    cxx_eval_constant_expression (ctx, body, false,
+                                 non_constant_p, overflow_p, jump_target);
   if (breaks (jump_target) || switches (jump_target))
     *jump_target = NULL_TREE;
   return NULL_TREE;
--- gcc/testsuite/g++.dg/cpp1y/constexpr-77467.C.jj     2016-09-05 
11:19:30.593750642 +0200
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-77467.C        2016-09-05 
11:37:11.929477518 +0200
@@ -0,0 +1,33 @@
+// PR c++/77467
+// { dg-do compile { target c++14 } }
+
+constexpr int
+foo (const int x, const unsigned n) noexcept
+{
+  switch (n)
+    {
+    case 0:
+      return 1;
+    case 1:
+      return x;
+    default:
+      const auto m = (n >> 1);
+      const auto y = foo (x, m);
+      return ((m << 1) == n) ? y * y : x * y * y;
+    }
+}
+
+static_assert (foo (3, 2) == 9, "");
+static_assert (foo (2, 3) == 8, "");
+
+constexpr int
+bar (int x)
+{
+  int a = x;
+  switch (x)
+    a = x + 1;
+  return a;
+}
+
+static_assert (bar (0) == 0, "");
+static_assert (bar (1) == 1, "");

        Jakub

Reply via email to