Handle C++14 constexpr flow control.
authorJason Merrill <jason@redhat.com>
Mon, 17 Nov 2014 19:08:07 +0000 (14:08 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Mon, 17 Nov 2014 19:08:07 +0000 (14:08 -0500)
* constexpr.c (cxx_eval_loop_expr, cxx_eval_switch_expr): New.
(cxx_eval_statement_list): New.
(cxx_eval_constant_expression): Handle LABEL_EXPR,
CASE_LABEL_EXPR, GOTO_EXPR, LOOP_EXPR, SWITCH_EXPR.  Handle jump
semantics of RETURN_EXPR.
(many functions): Add jump_target parameter.
(returns, breaks, continues, switches, label_matches): New.
* cp-tree.h (LABEL_DECL_BREAK, LABEL_DECL_CONTINUE): New.
* cp-gimplify.c (begin_bc_block): Set them.

From-SVN: r217670

gcc/cp/ChangeLog
gcc/cp/constexpr.c
gcc/cp/cp-gimplify.c
gcc/cp/cp-tree.h
gcc/testsuite/g++.dg/cpp1y/constexpr-loop1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/constexpr-return1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/constexpr-return2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/constexpr-switch1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/constexpr-switch2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/constexpr-switch3.C [new file with mode: 0644]

index e77e7a4169f06288d7fd4c0f29594733d13de2c1..18e06d82cd1e34a2750fa68ac4a382d931e162b6 100644 (file)
@@ -1,5 +1,16 @@
 2014-11-17  Jason Merrill  <jason@redhat.com>
 
+       Handle C++14 constexpr flow control.
+       * constexpr.c (cxx_eval_loop_expr, cxx_eval_switch_expr): New.
+       (cxx_eval_statement_list): New.
+       (cxx_eval_constant_expression): Handle LABEL_EXPR,
+       CASE_LABEL_EXPR, GOTO_EXPR, LOOP_EXPR, SWITCH_EXPR.  Handle jump
+       semantics of RETURN_EXPR.
+       (many functions): Add jump_target parameter.
+       (returns, breaks, continues, switches, label_matches): New.
+       * cp-tree.h (LABEL_DECL_BREAK, LABEL_DECL_CONTINUE): New.
+       * cp-gimplify.c (begin_bc_block): Set them.
+
        * cp-gimplify.c (genericize_cp_loop): Use LOOP_EXPR.
        (genericize_for_stmt): Handle null statement-list.
 
index 8881271de24270fac2678e96c2ce75e9c0512162..5b2565413b0ec8ddbd6f047bc2bf2006343d1140 100644 (file)
@@ -871,7 +871,7 @@ struct constexpr_ctx {
 static GTY (()) hash_table<constexpr_call_hasher> *constexpr_call_table;
 
 static tree cxx_eval_constant_expression (const constexpr_ctx *, tree,
-                                         bool, bool, bool *, bool *);
+                                         bool, bool, bool *, bool *, tree *);
 
 /* Compute a hash value for a constexpr call representation.  */
 
@@ -993,7 +993,8 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t,
     {
       args[i] = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, i),
                                              allow_non_constant, addr,
-                                             non_constant_p, overflow_p);
+                                             non_constant_p, overflow_p,
+                                             NULL);
       if (allow_non_constant && *non_constant_p)
        return t;
     }
@@ -1068,7 +1069,7 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t,
        }
       arg = cxx_eval_constant_expression (ctx, x, allow_non_constant,
                                          TREE_CODE (type) == REFERENCE_TYPE,
-                                         non_constant_p, overflow_p);
+                                         non_constant_p, overflow_p, NULL);
       /* Don't VERIFY_CONSTANT here.  */
       if (*non_constant_p && allow_non_constant)
        return;
@@ -1149,7 +1150,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
       /* Might be a constexpr function pointer.  */
       fun = cxx_eval_constant_expression (ctx, fun, allow_non_constant,
                                          /*addr*/false, non_constant_p,
-                                         overflow_p);
+                                         overflow_p, NULL);
       STRIP_NOPS (fun);
       if (TREE_CODE (fun) == ADDR_EXPR)
        fun = TREE_OPERAND (fun, 0);
@@ -1185,7 +1186,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
        {
          tree arg = convert_from_reference (get_nth_callarg (t, 1));
          return cxx_eval_constant_expression (ctx, arg, allow_non_constant,
-                                              addr, non_constant_p, overflow_p);
+                                              addr, non_constant_p,
+                                              overflow_p, NULL);
        }
       else if (TREE_CODE (t) == AGGR_INIT_EXPR
               && AGGR_INIT_ZERO_FIRST (t))
@@ -1281,7 +1283,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
              result = (cxx_eval_constant_expression
                        (&new_ctx, new_call.fundef->body,
                         allow_non_constant, addr,
-                        non_constant_p, overflow_p));
+                        non_constant_p, overflow_p, NULL));
            }
          else
            {
@@ -1321,8 +1323,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
              else
                ctx->values->put (res, NULL_TREE);
 
+             tree jump_target = NULL_TREE;
              cxx_eval_constant_expression (ctx, body, allow_non_constant,
-                                           addr, non_constant_p, overflow_p);
+                                           addr, non_constant_p, overflow_p,
+                                           &jump_target);
 
              if (VOID_TYPE_P (TREE_TYPE (res)))
                /* This can be null for a subobject constructor call, in
@@ -1454,7 +1458,8 @@ cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t,
   tree r;
   tree orig_arg = TREE_OPERAND (t, 0);
   tree arg = cxx_eval_constant_expression (ctx, orig_arg, allow_non_constant,
-                                          addr, non_constant_p, overflow_p);
+                                          addr, non_constant_p, overflow_p,
+                                          NULL);
   VERIFY_CONSTANT (arg);
   if (arg == orig_arg)
     return t;
@@ -1477,11 +1482,11 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
   tree lhs, rhs;
   lhs = cxx_eval_constant_expression (ctx, orig_lhs,
                                      allow_non_constant, addr,
-                                     non_constant_p, overflow_p);
+                                     non_constant_p, overflow_p, NULL);
   VERIFY_CONSTANT (lhs);
   rhs = cxx_eval_constant_expression (ctx, orig_rhs,
                                      allow_non_constant, addr,
-                                     non_constant_p, overflow_p);
+                                     non_constant_p, overflow_p, NULL);
   VERIFY_CONSTANT (rhs);
   if (lhs == orig_lhs && rhs == orig_rhs)
     return t;
@@ -1497,20 +1502,24 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t,
 static tree
 cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
                                 bool allow_non_constant, bool addr,
-                                bool *non_constant_p, bool *overflow_p)
+                                bool *non_constant_p, bool *overflow_p,
+                                tree *jump_target)
 {
   tree val = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
                                           allow_non_constant, addr,
-                                          non_constant_p, overflow_p);
+                                          non_constant_p, overflow_p,
+                                          NULL);
   VERIFY_CONSTANT (val);
   /* Don't VERIFY_CONSTANT the other operands.  */
   if (integer_zerop (val))
     return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
                                         allow_non_constant, addr,
-                                        non_constant_p, overflow_p);
+                                        non_constant_p, overflow_p,
+                                        jump_target);
   return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
                                       allow_non_constant, addr,
-                                      non_constant_p, overflow_p);
+                                      non_constant_p, overflow_p,
+                                      jump_target);
 }
 
 /* Subroutine of cxx_eval_constant_expression.
@@ -1524,7 +1533,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
   tree oldary = TREE_OPERAND (t, 0);
   tree ary = cxx_eval_constant_expression (ctx, oldary,
                                           allow_non_constant, addr,
-                                          non_constant_p, overflow_p);
+                                          non_constant_p, overflow_p, NULL);
   tree index, oldidx;
   HOST_WIDE_INT i;
   tree elem_type;
@@ -1534,7 +1543,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
   oldidx = TREE_OPERAND (t, 1);
   index = cxx_eval_constant_expression (ctx, oldidx,
                                        allow_non_constant, false,
-                                       non_constant_p, overflow_p);
+                                       non_constant_p, overflow_p, NULL);
   VERIFY_CONSTANT (index);
   if (addr && ary == oldary && index == oldidx)
     return t;
@@ -1565,7 +1574,8 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t,
          tree val = build_value_init (elem_type, tf_warning_or_error);
          return cxx_eval_constant_expression (ctx, val,
                                               allow_non_constant, addr,
-                                              non_constant_p, overflow_p);
+                                              non_constant_p, overflow_p,
+                                              NULL);
        }
 
       if (!allow_non_constant)
@@ -1611,7 +1621,7 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
   tree orig_whole = TREE_OPERAND (t, 0);
   tree whole = cxx_eval_constant_expression (ctx, orig_whole,
                                             allow_non_constant, addr,
-                                            non_constant_p, overflow_p);
+                                            non_constant_p, overflow_p, NULL);
   if (whole == orig_whole)
     return t;
   if (addr)
@@ -1670,7 +1680,7 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t,
   value = build_value_init (TREE_TYPE (t), tf_warning_or_error);
   return cxx_eval_constant_expression (ctx, value,
                                       allow_non_constant, addr,
-                                      non_constant_p, overflow_p);
+                                      non_constant_p, overflow_p, NULL);
 }
 
 /* Subroutine of cxx_eval_constant_expression.
@@ -1688,7 +1698,7 @@ cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t,
   HOST_WIDE_INT istart, isize;
   tree whole = cxx_eval_constant_expression (ctx, orig_whole,
                                             allow_non_constant, addr,
-                                            non_constant_p, overflow_p);
+                                            non_constant_p, overflow_p, NULL);
   tree start, field, value;
   unsigned HOST_WIDE_INT i;
 
@@ -1771,13 +1781,14 @@ cxx_eval_logical_expression (const constexpr_ctx *ctx, tree t,
   tree r;
   tree lhs = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
                                           allow_non_constant, addr,
-                                          non_constant_p, overflow_p);
+                                          non_constant_p, overflow_p, NULL);
   VERIFY_CONSTANT (lhs);
   if (tree_int_cst_equal (lhs, bailout_value))
     return lhs;
   gcc_assert (tree_int_cst_equal (lhs, continue_value));
   r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
-                                   allow_non_constant, addr, non_constant_p, overflow_p);
+                                   allow_non_constant, addr, non_constant_p,
+                                   overflow_p, NULL);
   VERIFY_CONSTANT (r);
   return r;
 }
@@ -1920,7 +1931,8 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t,
        CONSTRUCTOR_APPEND_ELT (*p, index, new_ctx.ctor);
       tree elt = cxx_eval_constant_expression (&new_ctx, value,
                                               allow_non_constant, addr,
-                                              non_constant_p, overflow_p);
+                                              non_constant_p, overflow_p,
+                                              NULL);
       /* Don't VERIFY_CONSTANT here.  */
       if (allow_non_constant && *non_constant_p)
        break;
@@ -2038,7 +2050,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
             we just pre-built above.  */
          eltinit = (cxx_eval_constant_expression
                     (&new_ctx, init, allow_non_constant,
-                     addr, non_constant_p, overflow_p));
+                     addr, non_constant_p, overflow_p, NULL));
        }
       else
        {
@@ -2052,7 +2064,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
          eltinit = force_rvalue (eltinit, tf_warning_or_error);
          eltinit = (cxx_eval_constant_expression
                     (&new_ctx, eltinit, allow_non_constant, addr,
-                     non_constant_p, overflow_p));
+                     non_constant_p, overflow_p, NULL));
        }
       if (*non_constant_p && !allow_non_constant)
        break;
@@ -2288,7 +2300,8 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
 {
   tree orig_op0 = TREE_OPERAND (t, 0);
   tree op0 = cxx_eval_constant_expression (ctx, orig_op0, allow_non_constant,
-                                          /*addr*/false, non_constant_p, overflow_p);
+                                          /*addr*/false, non_constant_p,
+                                          overflow_p, NULL);
   bool empty_base = false;
   tree r;
 
@@ -2301,7 +2314,7 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t,
 
   if (r)
     r = cxx_eval_constant_expression (ctx, r, allow_non_constant,
-                                     addr, non_constant_p, overflow_p);
+                                     addr, non_constant_p, overflow_p, NULL);
   else
     {
       tree sub = op0;
@@ -2403,7 +2416,8 @@ cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t,
     {
       args[i] = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, i),
                                              allow_non_constant, addr,
-                                             non_constant_p, overflow_p);
+                                             non_constant_p, overflow_p,
+                                             NULL);
       VERIFY_CONSTANT (args[i]);
     }
 
@@ -2436,7 +2450,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
   tree target = TREE_OPERAND (t, 0);
   target = cxx_eval_constant_expression (ctx, target,
                                         allow_non_constant, true,
-                                        non_constant_p, overflow_p);
+                                        non_constant_p, overflow_p, NULL);
   if (*non_constant_p)
     return t;
 
@@ -2512,7 +2526,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
 
   tree init = cxx_eval_constant_expression (&new_ctx, TREE_OPERAND (t, 1),
                                            allow_non_constant, false,
-                                           non_constant_p, overflow_p);
+                                           non_constant_p, overflow_p, NULL);
   if (target == object)
     /* The hash table might have moved since the get earlier.  */
     ctx->values->put (object, init);
@@ -2542,12 +2556,12 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
 
   /* The operand as an lvalue.  */
   op = cxx_eval_constant_expression (ctx, op, allow_non_constant, true,
-                                    non_constant_p, overflow_p);
+                                    non_constant_p, overflow_p, NULL);
 
   /* The operand as an rvalue.  */
   tree val = rvalue (op);
   val = cxx_eval_constant_expression (ctx, val, allow_non_constant, false,
-                                     non_constant_p, overflow_p);
+                                     non_constant_p, overflow_p, NULL);
   VERIFY_CONSTANT (val);
 
   /* The modified value.  */
@@ -2559,7 +2573,7 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
   /* Storing the modified value.  */
   tree store = build2 (MODIFY_EXPR, type, op, mod);
   cxx_eval_constant_expression (ctx, store, allow_non_constant,
-                               true, non_constant_p, overflow_p);
+                               true, non_constant_p, overflow_p, NULL);
 
   /* And the value of the expression.  */
   if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR)
@@ -2576,6 +2590,159 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
     return val;
 }
 
+/* Predicates for the meaning of *jump_target.  */
+
+static bool
+returns (tree *jump_target)
+{
+  return *jump_target
+    && TREE_CODE (*jump_target) == RETURN_EXPR;
+}
+
+static bool
+breaks (tree *jump_target)
+{
+  return *jump_target
+    && TREE_CODE (*jump_target) == LABEL_DECL
+    && LABEL_DECL_BREAK (*jump_target);
+}
+
+static bool
+continues (tree *jump_target)
+{
+  return *jump_target
+    && TREE_CODE (*jump_target) == LABEL_DECL
+    && LABEL_DECL_CONTINUE (*jump_target);
+}
+
+static bool
+switches (tree *jump_target)
+{
+  return *jump_target
+    && TREE_CODE (*jump_target) == INTEGER_CST;
+}
+
+/* Subroutine of cxx_eval_statement_list.  Determine whether the statement
+   at I matches *jump_target.  If we're looking for a case label and we see
+   the default label, copy I into DEFAULT_LABEL.  */
+
+static bool
+label_matches (tree *jump_target, tree_stmt_iterator i,
+              tree_stmt_iterator& default_label)
+{
+  tree stmt = tsi_stmt (i);
+  switch (TREE_CODE (*jump_target))
+    {
+    case LABEL_DECL:
+      if (TREE_CODE (stmt) == LABEL_EXPR
+         && LABEL_EXPR_LABEL (stmt) == *jump_target)
+       return true;
+      break;
+
+    case INTEGER_CST:
+      if (TREE_CODE (stmt) == CASE_LABEL_EXPR)
+       {
+         if (!CASE_LOW (stmt))
+           default_label = i;
+         else if (tree_int_cst_equal (*jump_target, CASE_LOW (stmt)))
+           return true;
+       }
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+  return false;
+}
+
+/* Evaluate a STATEMENT_LIST for side-effects.  Handles various jump
+   semantics, for switch, break, continue, and return.  */
+
+static tree
+cxx_eval_statement_list (const constexpr_ctx *ctx, tree t,
+                        bool allow_non_constant,
+                        bool *non_constant_p, bool *overflow_p,
+                        tree *jump_target)
+{
+  tree_stmt_iterator i;
+  tree_stmt_iterator default_label = tree_stmt_iterator();
+  for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
+    {
+    reenter:
+      tree stmt = tsi_stmt (i);
+      if (*jump_target)
+       {
+         if (TREE_CODE (stmt) == STATEMENT_LIST)
+           /* The label we want might be inside.  */;
+         else if (label_matches (jump_target, i, default_label))
+           /* Found it.  */
+           *jump_target = NULL_TREE;
+         else
+           continue;
+       }
+      cxx_eval_constant_expression (ctx, stmt,
+                                   allow_non_constant, false,
+                                   non_constant_p, overflow_p,
+                                   jump_target);
+      if (*non_constant_p)
+       break;
+      if (returns (jump_target) || breaks (jump_target))
+       break;
+    }
+  if (switches (jump_target) && !tsi_end_p (default_label))
+    {
+      i = default_label;
+      *jump_target = NULL_TREE;
+      goto reenter;
+    }
+  return NULL_TREE;
+}
+
+/* Evaluate a LOOP_EXPR for side-effects.  Handles break and return
+   semantics; continue semantics are covered by cxx_eval_statement_list.  */
+
+static tree
+cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
+                   bool allow_non_constant,
+                   bool *non_constant_p, bool *overflow_p,
+                   tree *jump_target)
+{
+  tree body = TREE_OPERAND (t, 0);
+  while (true)
+    {
+      cxx_eval_statement_list (ctx, body, allow_non_constant,
+                              non_constant_p, overflow_p, jump_target);
+      if (returns (jump_target) || breaks (jump_target))
+       break;
+    }
+  if (breaks (jump_target))
+    *jump_target = NULL_TREE;
+  return NULL_TREE;
+}
+
+/* Evaluate a SWITCH_EXPR for side-effects.  Handles switch and break jump
+   semantics.  */
+
+static tree
+cxx_eval_switch_expr (const constexpr_ctx *ctx, tree t,
+                     bool allow_non_constant,
+                     bool *non_constant_p, bool *overflow_p,
+                     tree *jump_target)
+{
+  tree cond = TREE_OPERAND (t, 0);
+  cond = cxx_eval_constant_expression (ctx, cond, allow_non_constant, false,
+                                      non_constant_p, overflow_p, NULL);
+  VERIFY_CONSTANT (cond);
+  *jump_target = cond;
+
+  tree body = TREE_OPERAND (t, 1);
+  cxx_eval_statement_list (ctx, body, allow_non_constant,
+                          non_constant_p, overflow_p, jump_target);
+  if (breaks (jump_target) || switches (jump_target))
+    *jump_target = NULL_TREE;
+  return NULL_TREE;
+}
+
 /* Attempt to reduce the expression T to a constant value.
    On failure, issue diagnostic and return error_mark_node.  */
 /* FIXME unify with c_fully_fold */
@@ -2584,7 +2751,8 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
 static tree
 cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
                              bool allow_non_constant, bool addr,
-                             bool *non_constant_p, bool *overflow_p)
+                             bool *non_constant_p, bool *overflow_p,
+                             tree *jump_target)
 {
   constexpr_ctx new_ctx;
   tree r = t;
@@ -2639,6 +2807,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case FUNCTION_DECL:
     case TEMPLATE_DECL:
     case LABEL_DECL:
+    case LABEL_EXPR:
+    case CASE_LABEL_EXPR:
       return t;
 
     case PARM_DECL:
@@ -2683,7 +2853,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
          {
            init = cxx_eval_constant_expression (ctx, init,
                                                 allow_non_constant, false,
-                                                non_constant_p, overflow_p);
+                                                non_constant_p, overflow_p,
+                                                NULL);
            ctx->values->put (r, init);
          }
        else if (ctx == &new_ctx)
@@ -2721,7 +2892,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
         initialization of a temporary.  */
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
                                        allow_non_constant, false,
-                                       non_constant_p, overflow_p);
+                                       non_constant_p, overflow_p, NULL);
       if (!*non_constant_p)
        /* Adjust the type of the result to the type of the temporary.  */
        r = adjust_temp_type (TREE_TYPE (t), r);
@@ -2734,7 +2905,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
             not the side-effect of the initialization.  */
          r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
                                            allow_non_constant, false,
-                                           non_constant_p, overflow_p);
+                                           non_constant_p, overflow_p, NULL);
          break;
        }
       /* else fall through */
@@ -2746,10 +2917,16 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case SCOPE_REF:
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
                                        allow_non_constant, addr,
-                                       non_constant_p, overflow_p);
+                                       non_constant_p, overflow_p, NULL);
       break;
 
     case RETURN_EXPR:
+      r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
+                                       allow_non_constant, addr,
+                                       non_constant_p, overflow_p, NULL);
+      *jump_target = t;
+      break;
+
     case NON_LVALUE_EXPR:
     case TRY_CATCH_EXPR:
     case CLEANUP_POINT_EXPR:
@@ -2759,7 +2936,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case EH_SPEC_BLOCK:
       r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
                                        allow_non_constant, addr,
-                                       non_constant_p, overflow_p);
+                                       non_constant_p, overflow_p,
+                                       jump_target);
       break;
 
       /* These differ from cxx_eval_unary_expression in that this doesn't
@@ -2776,7 +2954,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
        tree op = cxx_eval_constant_expression (ctx, oldop,
                                                allow_non_constant,
                                                /*addr*/true,
-                                               non_constant_p, overflow_p);
+                                               non_constant_p, overflow_p,
+                                               NULL);
        /* Don't VERIFY_CONSTANT here.  */
        if (*non_constant_p)
          return t;
@@ -2827,15 +3006,18 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
        if ((TREE_CODE (op0) == TARGET_EXPR && op1 == TARGET_EXPR_SLOT (op0))
            || TREE_CODE (op1) == EMPTY_CLASS_EXPR)
          r = cxx_eval_constant_expression (ctx, op0, allow_non_constant,
-                                           addr, non_constant_p, overflow_p);
+                                           addr, non_constant_p, overflow_p,
+                                           jump_target);
        else
          {
            /* Check that the LHS is constant and then discard it.  */
            cxx_eval_constant_expression (ctx, op0, allow_non_constant,
-                                         false, non_constant_p, overflow_p);
+                                         false, non_constant_p, overflow_p,
+                                         jump_target);
            op1 = TREE_OPERAND (t, 1);
            r = cxx_eval_constant_expression (ctx, op1, allow_non_constant,
-                                             addr, non_constant_p, overflow_p);
+                                             addr, non_constant_p, overflow_p,
+                                             jump_target);
          }
       }
       break;
@@ -2929,7 +3111,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
     case COND_EXPR:
     case VEC_COND_EXPR:
       r = cxx_eval_conditional_expression (ctx, t, allow_non_constant, addr,
-                                          non_constant_p, overflow_p);
+                                          non_constant_p, overflow_p,
+                                          jump_target);
       break;
 
     case CONSTRUCTOR:
@@ -2960,7 +3143,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
        tree oldop = TREE_OPERAND (t, 0);
        tree op = cxx_eval_constant_expression (ctx, oldop,
                                                allow_non_constant, addr,
-                                               non_constant_p, overflow_p);
+                                               non_constant_p, overflow_p,
+                                               NULL);
        if (*non_constant_p)
          return t;
        if (POINTER_TYPE_P (TREE_TYPE (t))
@@ -2992,25 +3176,16 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
       return t;
 
     case STATEMENT_LIST:
-      {
-       new_ctx = *ctx;
-       new_ctx.ctor = new_ctx.object = NULL_TREE;
-       tree_stmt_iterator i;
-       for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
-         {
-           cxx_eval_constant_expression (&new_ctx, tsi_stmt (i),
-                                         allow_non_constant, false,
-                                         non_constant_p, overflow_p);
-           if (*non_constant_p)
-             break;
-         }
-      }
-      break;
+      new_ctx = *ctx;
+      new_ctx.ctor = new_ctx.object = NULL_TREE;
+      return cxx_eval_statement_list (&new_ctx, t, allow_non_constant,
+                                     non_constant_p, overflow_p, jump_target);
 
     case BIND_EXPR:
       return cxx_eval_constant_expression (ctx, BIND_EXPR_BODY (t),
                                           allow_non_constant, addr,
-                                          non_constant_p, overflow_p);
+                                          non_constant_p, overflow_p,
+                                          jump_target);
 
     case PREINCREMENT_EXPR:
     case POSTINCREMENT_EXPR:
@@ -3058,16 +3233,24 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
          gcc_assert (same_type_ignoring_top_level_qualifiers_p
                      (TREE_TYPE (t), TREE_TYPE (ctor)));
          return cxx_eval_constant_expression
-           (ctx, ctor, allow_non_constant, addr, non_constant_p, overflow_p);
+           (ctx, ctor, allow_non_constant, addr,
+            non_constant_p, overflow_p, NULL);
        }
       break;
 
     case GOTO_EXPR:
+      *jump_target = TREE_OPERAND (t, 0);
+      gcc_assert (breaks (jump_target) || continues (jump_target));
+      break;
+
     case LOOP_EXPR:
+      cxx_eval_loop_expr (ctx, t, allow_non_constant,
+                         non_constant_p, overflow_p, jump_target);
+      break;
+
     case SWITCH_EXPR:
-      if (!allow_non_constant)
-       sorry ("%qs in constant expression", get_tree_code_name (TREE_CODE (t)));
-      *non_constant_p = true;
+      cxx_eval_switch_expr (ctx, t, allow_non_constant,
+                           non_constant_p, overflow_p, jump_target);
       break;
 
     default:
@@ -3128,7 +3311,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
     }
 
   r = cxx_eval_constant_expression (&ctx, r, allow_non_constant,
-                                   false, &non_constant_p, &overflow_p);
+                                   false, &non_constant_p, &overflow_p, NULL);
 
   verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p);
 
@@ -3205,7 +3388,7 @@ is_sub_constant_expr (tree t)
   hash_map <tree, tree> map;
   ctx.values = &map;
   cxx_eval_constant_expression (&ctx, t, true, false, &non_constant_p,
-                               &overflow_p);
+                               &overflow_p, NULL);
   return !non_constant_p && !overflow_p;
 }
 
index 81b26d2203dd8cc4ea88398b0b8019b7bf577b2d..82be90be437f3886aad5cdb3fd3b73b5bdf62550 100644 (file)
@@ -74,6 +74,10 @@ begin_bc_block (enum bc_t bc, location_t location)
   tree label = create_artificial_label (location);
   DECL_CHAIN (label) = bc_label[bc];
   bc_label[bc] = label;
+  if (bc == bc_break)
+    LABEL_DECL_BREAK (label) = true;
+  else
+    LABEL_DECL_CONTINUE (label) = true;
   return label;
 }
 
index adc8aa72d973b4c1582f715d79008c18a6e80dfa..d3722d73f61148de1c69e06d1f2c796c5f76f61d 100644 (file)
@@ -148,12 +148,14 @@ c-common.h, not after.
       DECL_LOCAL_FUNCTION_P (in FUNCTION_DECL)
       DECL_MUTABLE_P (in FIELD_DECL)
       DECL_DEPENDENT_P (in USING_DECL)
+      LABEL_DECL_BREAK (in LABEL_DECL)
    1: C_TYPEDEF_EXPLICITLY_SIGNED (in TYPE_DECL).
       DECL_TEMPLATE_INSTANTIATED (in a VAR_DECL or a FUNCTION_DECL)
       DECL_MEMBER_TEMPLATE_P (in TEMPLATE_DECL)
       USING_DECL_TYPENAME_P (in USING_DECL)
       DECL_VLA_CAPTURE_P (in FIELD_DECL)
       DECL_ARRAY_PARAMETER_P (in PARM_DECL)
+      LABEL_DECL_CONTINUE (in LABEL_DECL)
    2: DECL_THIS_EXTERN (in VAR_DECL or FUNCTION_DECL).
       DECL_IMPLICIT_TYPEDEF_P (in a TYPE_DECL)
    3: DECL_IN_AGGR_P.
@@ -3243,6 +3245,14 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
 #define DECL_LOCAL_FUNCTION_P(NODE) \
   DECL_LANG_FLAG_0 (FUNCTION_DECL_CHECK (NODE))
 
+/* Nonzero if NODE is the target for genericization of 'break' stmts.  */
+#define LABEL_DECL_BREAK(NODE) \
+  DECL_LANG_FLAG_0 (LABEL_DECL_CHECK (NODE))
+
+/* Nonzero if NODE is the target for genericization of 'continue' stmts.  */
+#define LABEL_DECL_CONTINUE(NODE) \
+  DECL_LANG_FLAG_1 (LABEL_DECL_CHECK (NODE))
+
 /* True if NODE was declared with auto in its return type, but it has
    started compilation and so the return type might have been changed by
    return type deduction; its declared return type should be found in
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-loop1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-loop1.C
new file mode 100644 (file)
index 0000000..fc5d4f8
--- /dev/null
@@ -0,0 +1,13 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+  int j = 0;
+  for (; i > 0; --i)
+    ++j;
+  return j;
+}
+
+constexpr int i = f(42);
+#define SA(X) static_assert((X),#X)
+SA(i==42);
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-return1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-return1.C
new file mode 100644 (file)
index 0000000..b114e21
--- /dev/null
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+  return 24;
+  return 36;
+}
+
+constexpr int i = f(42);
+#define SA(X) static_assert((X),#X)
+SA(i==24);
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-return2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-return2.C
new file mode 100644 (file)
index 0000000..ae2628d
--- /dev/null
@@ -0,0 +1,7 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+}
+
+constexpr int i = f(42);       // { dg-error "flows off the end" }
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-switch1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-switch1.C
new file mode 100644 (file)
index 0000000..add7323
--- /dev/null
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+  switch (i)
+    {
+    case 1:
+      return 42;
+    default:
+      return 0;
+    }
+}
+
+constexpr int i = f(1);
+#define SA(X) static_assert((X),#X)
+SA(i==42);
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-switch2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-switch2.C
new file mode 100644 (file)
index 0000000..a459a5e
--- /dev/null
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+  int j = 0;
+  switch (i)
+    {
+    case 1:
+      j = 42;
+      break;
+    default:
+      j = 24;
+      break;
+    }
+  return j;
+}
+
+constexpr int i = f(1);
+#define SA(X) static_assert((X),#X)
+SA(i==42);
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-switch3.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-switch3.C
new file mode 100644 (file)
index 0000000..1aa1cf6
--- /dev/null
@@ -0,0 +1,20 @@
+// { dg-do compile { target c++14 } }
+
+constexpr int f (int i)
+{
+  int j = 0;
+  switch (i)
+    {
+    case 1:
+      j = 42;
+      break;
+    default:
+      j = 24;
+      break;
+    }
+  return j;
+}
+
+constexpr int i = f(2);
+#define SA(X) static_assert((X),#X)
+SA(i==24);